测试视图控制器 View Controllers
作者:网络转载 发布时间:[ 2014/3/17 14:05:37 ] 推荐标签:测试 视图测试
测试 Data Source
好了,让我们开始吧。我们已经通过拆分 view controller 让测试工作变得更轻松了。现在我们要测试 ArrayDataSource。首先我们新建一个空的,基本的测试类。我们把接口和实现都放到一个文件里;也没有哪个地方需要包含 @interface ,放到一个文件会显得更加漂亮和整洁。
#import "PhotoDataTestCase.h" @interface ArrayDataSourceTest : PhotoDataTestCase @end @implementation ArrayDataSourceTest - (void)testNothing; { STAssertTrue(YES, @""); } @end
这个类没做什么事,只是展示了基本的设置。当我们运行这个测试时,-testNothing 方法将会运行。特别地,STAssert 宏将会做琐碎的检查。注意,前缀 ST 源自于 SenTestingKit。这些宏和 Xcode 集成,会把失败显示到 Issues navigator 中。
第一个测试
我们现在把 testNothing 替换成一个简单、真正的测试:
- (void)testInitializing; { STAssertNil([[ArrayDataSource alloc] init], @"Should not be allowed."); TableViewCellConfigureBlock block = ^(UITableViewCell *a, id b){}; id obj1 = [[ArrayDataSource alloc] initWithItems:@[] cellIdentifier:@"foo" configureCellBlock:block]; STAssertNotNil(obj1, @""); }
实践 Mocking
接着,我们想测试 ArrayDataSource 实现的方法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
为此,我们创建一个测试方法:
- (void)testCellConfiguration;
首先,创建一个 data source:
__block UITableViewCell *configuredCell = nil; __block id configuredObject = nil; TableViewCellConfigureBlock block = ^(UITableViewCell *a, id b){ configuredCell = a; configuredObject = b; }; ArrayDataSource *dataSource = [[ArrayDataSource alloc] initWithItems:@[@"a", @"b"] cellIdentifier:@"foo" configureCellBlock:block];
注意,configureCellBlock 除了存储对象以外什么都没做,这可以让我们可以更简单地测试它。
然后,我们为 table view 创建一个 mock 对象:
id mockTableView = [OCMockObject mockForClass:[UITableView class]];
Data source 将在传进来的 table view 上调用 -dequeueReusableCellWithIdentifier:forIndexPath: 方法。我们将告诉 mock object 当它收到这个消息时要做什么。首先创建一个 cell,然后设置 mock。
UITableViewCell *cell = [[UITableViewCell alloc] init]; NSIndexPath* indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; [[[mockTableView expect] andReturn:cell] dequeueReusableCellWithIdentifier:@"foo" forIndexPath:indexPath];
第一次看到它可能会觉得有点迷惑。我们在这里所做的,是让 mock 记录特定的调用。Mock 不是一个真正的 table view;我们只是假装它是。-expect 方法允许我们设置一个 mock,让它知道当这个方法调用时要做什么。
另外,-expect 方法也告诉 mock 这个调用必须发生。当我们稍后在 mock 上调用 -verify 时,如果那个方法没有被调用过,测试会失败。相应地,-stub 方法也用来设置 mock 对象,但它不关心方法是否被调用过。
现在,我们要触发代码运行。我们调用我们希望测试的方法。
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; id result = [dataSource tableView:mockTableView cellForRowAtIndexPath:indexPath];
然后我们测试是否一切正常:
STAssertEquals(result, cell, @"Should return the dummy cell."); STAssertEquals(configuredCell, cell, @"This should have been passed to the block."); STAssertEqualObjects(configuredObject, @"a", @"This should have been passed to the block."); [mockTableView verify];
STAssert 宏测试值的相等性。注意,前两个测试,我们通过比较指针来完成;我们不想使用 -isEqual:。我们实际希望测试的是 result,cell 和 configuredCell 都是同一个对象。第三个测试要用 -isEqual:,后我们调用 mock 的 -verify 方法。
注意,在示例程序中,我们是这样设置 mock 的:
id mockTableView = [self autoVerifiedMockForClass:[UITableView class]];
这是我们测试基类中的一个方便的封装,它会在测试后自动调用 -verify 方法。
测试 UITableViewController
下面,我们转向 PhotosViewController。它是个 UITableViewController 的子类,它使用了我们刚才测试过的 data source。View controller 剩下的代码已经相当简单了。
我们想测试点击 cell 后把我们带到详情页面,即一个 PhotoViewController 的实例被 push 到 navigation controller 里面。我们再次使用 mocking 来让测试尽可能不依赖于其他部分。
首先我们创建一个 UINavigationController 的 mock:
id mockNavController = [OCMockObject mockForClass:[UINavigationController class]];
接下来,我们要使用 partial mocking。我们希望 PhotosViewController 实例的 navigationController 返回 mockNavController。我们不能直接设置 navigation controller,所以我们简单地对 PhotosViewController 实例 stub 这个方法,让它返回 mockNavController 可以了。
PhotosViewController *photosViewController = [[PhotosViewController alloc] init]; id photosViewControllerMock = [OCMockObject partialMockForObject:photosViewController]; [[[photosViewControllerMock stub] andReturn:mockNavController] navigationController];
现在,任何时候对 photosViewController 调用 -navigationController 方法,都会返回 mockNavController。这是个强大的技巧,OCMock 有这种本领。
现在,我们要告诉 navigation controller mock 我们调用的期望,即,一个 photo 不为 nil 的 detail view controller。
UIViewController* viewController = [OCMArg checkWithBlock:^BOOL(id obj) { PhotoViewController *vc = obj; return ([vc isKindOfClass:[PhotoViewController class]] && (vc.photo != nil)); }]; [[mockNavController expect] pushViewController:viewController animated:YES];
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11