测试视图控制器 View Controllers
作者:网络转载 发布时间:[ 2014/3/17 14:05:37 ] 推荐标签:测试 视图测试
现在,我们触发 view 加载,并且模拟一行被点击:
UIView *view = photosViewController.view; STAssertNotNil(view, @""); NSIndexPath* indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; [photosViewController tableView:photosViewController.tableView didSelectRowAtIndexPath:indexPath];
后我们验证 mocks 上期望的方法被调用过:
[mockNavController verify]; [photosViewControllerMock verify];
现在我们有了一个测试,用来测试和 navigation controller 的交互,以及正确 view controller 的创建。
又一次地,我们在示例程序中使用了便捷的方法:
- (id)autoVerifiedMockForClass:(Class)aClass; - (id)autoVerifiedPartialMockForObject:(id)object;
于是,我们不需要记住调用 -verify。
进一步探索
像你从上面看到的那样,partial mocking 非常强大。如果你看看 -[PhotosViewController setupTableView] 方法的源码,你会看到它是如何从 app delegate 中取出 model 对象的。
NSArray *photos = [AppDelegate sharedDelegate].store.sortedPhotos;
上面的测试依赖于这行代码。打破这种依赖的一种方式是再次使用 partial mocking,让 app delegate 返回预定义的数据,像这样:
id storeMock; // assume we've set this up id appDelegate = [AppDelegate sharedDelegate] id appDelegateMock = [OCMockObject partialMockForObject:appDelegate]; [[[appDelegateMock stub] andReturn:storeMock] store];
现在,无论 [AppDelegate sharedDelegate].store 时候调用过,它也会返回 storeMock。可以把它发挥到。确保让你的测试尽可能保持简单,除非确实有复杂的需要。
牢记的事
Partial mocks 会修改 mocking 的对象,并且在 mocks 的生存期一直有效。你可以通过提前调用 [aMock stopMocking] 来停止这种行为。大多数时候,你希望 partial mock 在整个测试期间都保持有效。确保在测试方法后放置 [aMock verify]。否则 ARC 会过早 dealloc 这个 mock。而且不管怎样,你都希望加上 -verify。
测试 NIB 加载
PhotoCell 设置在一个 NIB 中,我们可以写一个简单的测试来检查 outlets 设置得是否正确。我们来回顾一下 PhotoCell 类:
@interface PhotoCell : UITableViewCell + (UINib *)nib; @property (weak, nonatomic) IBOutlet UILabel* photoTitleLabel; @property (weak, nonatomic) IBOutlet UILabel* photoDateLabel; @end
我们的简单测试的实现看上去是这样:
@implementation PhotoCellTests - (void)testNibLoading; { UINib *nib = [PhotoCell nib]; STAssertNotNil(nib, @""); NSArray *a = [nib instantiateWithOwner:nil options:@{}]; STAssertEquals([a count], (NSUInteger) 1, @""); PhotoCell *cell = a[0]; STAssertTrue([cell isMemberOfClass:[PhotoCell class]], @""); // Check that outlets are set up correctly: STAssertNotNil(cell.photoTitleLabel, @""); STAssertNotNil(cell.photoDateLabel, @""); } @end
非常基础,但是它能工作。
值得一提的是,当有什么发生变动时,测试和相应的类或 nib 需要同时更新。这是事实。你需要把它和 outlets 变化的可能性做权衡。如果你用了 .xib 文件,你可能要注意了,这是经常发生的事。
关于 Class 和 Injection
我们已经从与 Xcode 集成得知,测试 bundle 会注入到应用程序中。省略注入的如何工作的细节(它本身是个巨大的话题),简单地说:注入是把待注入的 bundle(我们的测试 bundle)中的 Objective-C 类添加到运行的应用程序中。这很好,因为这样允许我们运行测试了。
还有一件事会很让人迷惑,那是如果我们同时把一个类添加到应用程序和测试 bundle中。如果在上面的示例程序中,(偶然)把 PhotoCell 类添加到测试 bundle 和应用程序,然后在测试 bundle 中调用 [PhotoCell class] 会返回一个不同的指针(你应用程序中的那个类)。于是我们的测试将会失败:
STAssertTrue([cell isMemberOfClass:[PhotoCell class]], @"");
再一次声明:注入很复杂。你应该避免:不要把应用程序中的 .m 文件添加到测试 target 中。否则你会得到预想不到的行为。
额外的思考
如果你使用一个持续集成的解决方案,让你的测试启动和运行是一个好主意。详细的描述超过了本文的范围。这些脚本通过 RunUnitTests 脚本触发。还有个 TEST_AFTER_BUILD 环境变量。
一个有趣的选择是创建单独的测试 bundle 来自动化性能测试。你可以在测试方法里做任何你想做的。定时调用一些方法并使用 STAssert 来检查它们是否在特定阈值里面是一种选择。
相关推荐
更新发布
功能测试和接口测试的区别
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