Ios 用于异步测试的XCTest和nsrunlop
我在很多地方(包括苹果开发论坛)都看到,为了测试异步操作,一些开发人员建议抓住当前的运行循环,让它运行一段时间,以强制调用异步块。e、 gIos 用于异步测试的XCTest和nsrunlop,ios,xcode,unit-testing,asynchronous,nsrunloop,Ios,Xcode,Unit Testing,Asynchronous,Nsrunloop,我在很多地方(包括苹果开发论坛)都看到,为了测试异步操作,一些开发人员建议抓住当前的运行循环,让它运行一段时间,以强制调用异步块。e、 g __block id returnedModel = nil; BOOL result = [binder fetchAndBind:... successBlock:^(id *model) { returnModel = model; } errorBlock:n
__block id returnedModel = nil;
BOOL result = [binder fetchAndBind:...
successBlock:^(id *model) { returnModel = model; }
errorBlock:nil];
NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:10.0f];
BOOL isModelReturned = (returnedModel != nil);
while (!isModelReturned && [loopUntil timeIntervalSinceNow] > 0)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:loopUntil];
isModelReturned = (returnedModel != nil);
}
上述实现有不同的互联网风格,但概念是相同的。一些人正在使用调度组
,等等
问题:
- 苹果有关于测试异步操作的文档吗(我找不到)
- 从非官方来源我读到单元测试是 在他们正在运行的运行循环中自包含。所以他们不是 应该像上面那样对待。这是真的吗?是否有记录 苹果在什么地方
- 对于Xcode 5.1,上述内插或
会导致调度组
。是不是因为单元测试在它们自己的线程中是自包含的,不应该这样对待EXC\u错误访问(code=2,address=0xd)
Class a
中的一个方法,它的公共API将一个NSArray
作为输入崩溃,因为测试模拟了一个对象,让运行循环继续,然后该对象开始与Class a
交互,并且因为它被模拟,所以它在字典中传递然而,如果不强制运行循环继续,那么对象将在以后被取消模拟,并且每个测试都会很愉快
我个人认为没有理由测试async
。有一个操作以异步方式运行。需要测试的是操作/功能,而不是异步
我正在寻找一些参考资料或文档(苹果提供的)来清楚地讨论异步单元测试,单元测试的运行循环是否可以强制继续,或者使用XCTests测试异步操作的推荐方法是什么
谢谢
编辑:
在Xcode 6中,XCTest框架附带异步测试宏。我把这个问题留在这里作为参考。关于示例代码,有一些误解: 首先,如果完成处理程序将在不同于调用站点执行的线程上执行,编译器将创建“未定义行为”的代码。这是由于修改线程“A”中的变量
returnedModel
,并读取线程“M”中的值。这是一个经典的“数据竞赛”,它产生了未定义的行为(请阅读更多的C和C++规范)。
\uu块
修饰符可能会缓解这个问题,但我不认为clang在这里采取特殊行动。在最坏的情况下,读取值的线程(主线程)从未“看到”通过处理程序执行的值更新,或者读取“垃圾”
这种方法的另一个问题是需要更透彻地理解运行循环实际上是如何工作的。在您的示例中,在最坏的情况下,运行循环的方法runMode:beforeDate:
将仅在超时过期时(即10秒后)返回。仅当在此模式下处理了事件时,它可能会更早返回-可能与测试代码无关
简而言之,这种方法并不适合完成任务。但其他“口味”可能确实有效
根据你的问题:
问题1:没有
原因可能是,XCTest实际上相当古老(它只是SenTest的另一个名称),而它发明时的代码可能没有像“异步操作”、“块”和“完成处理程序”这样奇特的东西。所以这个任务没有内置的解决方案
问题2:我不太理解这些问题。但是我们可以假设“matchers”(又名“assert something”)在测试失败时使用异常。这些需要在主线程上执行,其中有一个由底层测试实现实现的catch处理程序。也许XCTest不使用异常——但是,其他单元测试库可能确实使用异常——例如“Cedar”。这意味着,如果您在某个队列上执行一个完成处理程序,并且匹配器抛出一个异常,那么它必须在主线程上执行。(糟糕透顶)
问题3:可能是例外问题?但我不知道。可能还有另一个问题。您可以提供更多信息
其他“副作用”可能是“比赛条件”或其他问题。但除非你提供更详细的信息,我猜;)
是否需要“测试异步”实际上取决于您实际测试的内容:
例如,如果您使用一个著名的第三方网络库,它有一个完成处理程序,您真的想测试是否调用该处理程序吗?(可能不会,因为您不想实际测试网络库)
但是,如果您实现了自己的异步操作,该操作通过完成处理程序报告结果,您实际上可能需要测试是否将调用完成处理程序。感谢您的全面响应。同意对示例代码存在误解。只要在SO或Google中搜索它,你就会找到它的例子。至少在我所看到的使用此模式的代码中,完成块保证在主线程上被调用。虽然您关于
是否实现了自己的异步操作…
的论点是有道理的,但我还是会仔细研究一下,看看是我真正实现了异步还是由操作系统提供的。例如,仅仅因为代码使用了NSOperation
,并不意味着如果坚持NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode,beforeDate:NSDate(timeIntervalSinceNow:10))
则单元测试必须适应异步操作