【发布时间】:2014-01-29 05:54:47
【问题描述】:
我正在使用XCTest 和OCMock 为iOS 应用程序编写单元测试,我需要指导如何最好地设计一个单元测试来验证方法是否导致NSTimer 被启动。
被测代码:
- (void)start {
...
self.timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(tick:)
userInfo:nil
repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
...
}
我要测试的是计时器是用正确的参数创建的,并且计时器被安排在运行循环上运行。
我想到了以下我不满意的选项:
- 实际上等待计时器触发。 (我不喜欢它的原因:糟糕的单元测试实践。它更像是一个缓慢的集成测试。)
- 将计时器启动代码提取到私有方法中,在类扩展文件中将该私有方法公开给单元测试,并使用模拟期望来验证私有方法是否被调用。 (我不喜欢它的原因:它验证方法是否被调用,但不是该方法实际设置计时器以正确运行。另外,公开私有方法不是一个好习惯。)
- 为被测代码提供一个模拟
NSTimer。 (我不喜欢它的原因:无法验证它是否真的被安排运行,因为计时器是通过运行循环而不是从某些NSTimerstart 方法启动的。) - 提供一个模拟
NSRunLoop并验证addTimer:forMode: 是否被调用。 (我不喜欢它的原因:我必须在运行循环中提供一个接口?这看起来很古怪。)
有人可以提供一些单元测试指导吗?
【问题讨论】:
-
您不必验证它是否实际上已安排运行。调度和运行位不是您的代码;即使你对它们进行了单元测试并且它们失败了,你能做些什么呢?您只需要确保您的代码在该点之前完成它需要做的事情,并与框架正确连接。因此,我认为第三个是答案——告诉
NSTimer类对象的模拟以期望timerWithTimeInterval:... -
嘲讽
NSRunLoop也有道理; Mike Ash 有一篇文章可能会对那里有所帮助:mikeash.com/pyblog/… -
乔希,感谢您的指导!我最终对这个问题进行了更多思考,您对“调度和运行位不是您的代码”的评论提醒我,我曾在某处读过,“不要测试 Apple 的实现”。直到现在才有意义。我试图设计我的测试来涵盖所有可能的场景,但是当你使用别人的代码做事情时你不能这样做,你所能做的就是测试发生交互。
-
很高兴能为您提供帮助。希望你会考虑用你解决这个问题的方式写一个答案。
标签: ios objective-c unit-testing nstimer