Xctest XTestExpection:如何避免在等待上下文结束后调用fully方法?

Xctest XTestExpection:如何避免在等待上下文结束后调用fully方法?,xctest,Xctest,我正在使用Xcode 6的新异步测试功能。当异步任务在超时之前结束时,一切正常。但是,如果任务花费的时间超过超时时间,事情就会变得更加复杂 以下是我如何进行测试: @接口异步测试用例:XCTestCase@end @异步测试用例的实现 //在真实场景中,异步任务显然更复杂。 -(void)StartSynchronousTaskWithDuration:(NSTimeInterval)duration completionHandler:(void(^)(id结果,NSError*error))

我正在使用Xcode 6的新异步测试功能。当异步任务在超时之前结束时,一切正常。但是,如果任务花费的时间超过超时时间,事情就会变得更加复杂

以下是我如何进行测试:

@接口异步测试用例:XCTestCase@end
@异步测试用例的实现
//在真实场景中,异步任务显然更复杂。
-(void)StartSynchronousTaskWithDuration:(NSTimeInterval)duration completionHandler:(void(^)(id结果,NSError*error))completionHandler
{
在(调度时间(现在调度时间,(int64_t)(持续时间*NSEC_/秒))之后调度,调度获取主队列()^{
completionHandler([NSObject new],nil);
});
}
-(无效)测试1tasklongerthantimeout
{
XTestExpection*expectation=[自我期望与描述:@“测试1:任务长于超时”];
[自启动同步任务,持续时间:4 completionHandler:^(id结果,N错误*错误){
xctasertnotnil(结果);
xctasertnil(错误);
[期望实现];
}];
[self-waitForExpectationsWithTimeout:2处理程序:nil];
}
-(无效)Test2TaskShortThanTimeout
{
XTestExpection*expectation=[自我期望与描述:@“测试2:任务短于超时”];
[自启动同步任务,持续时间:5 completionHandler:^(id结果,N错误*错误){
xctasertnotnil(结果);
xctasertnil(错误);
[期望实现];
}];
[self-waitForExpectationsWithTimeout:10处理程序:nil];
}
@结束
不幸的是,在超时过期后调用
fulfill
方法会导致测试套件崩溃,并出现以下错误:

API冲突-在等待上下文结束后调用-[XTestExpectation fulfill]

当然,我可以在调用
fulfill
方法之前检查测试是否完成,如下所示:

-(void)test1TaskLongerThanTimeout
{
XTestExpection*expectation=[自我期望与描述:@“测试1:任务长于超时”];
__块布尔测试完成=否;
[自启动同步任务,持续时间:4 completionHandler:^(id结果,N错误*错误){
如果(测试完成){
返回;
}
xctasertnotnil(结果);
xctasertnil(错误);
[期望实现];
}];
[self-waitForExpectationsWithTimeout:2处理程序:^(N错误*错误){
testIsFinished=是;
}];
}

但这似乎过于复杂,使测试更难阅读。我错过什么了吗?有没有一种更简单的方法来解决这个问题?

是的,有一种更简单的方法来避免这个API冲突问题:只需将您的期望变量声明为
\u-weak
。虽然没有明确的文档记录,但当超时过期时,将释放期望值。因此,如果任务花费的时间超过超时时间,则调用任务完成处理程序时,期望变量将为nil。因此,
fulfill
方法将在nil上调用,而不执行任何操作

-(void)test1TaskLongerThanTimeout
{
__弱XctestExpection*expectation=[自我期望与描述:@“测试1:任务长于超时”];
[自启动同步任务,持续时间:4 completionHandler:^(id结果,N错误*错误){
xctasertnotnil(结果);
xctasertnil(错误);
[期望实现];
}];
[self-waitForExpectationsWithTimeout:2处理程序:nil];
}

我遇到了同样的问题,但我需要上面答案的快速版本

我正在为OSX开发OpenStack Swift驱动器。当使用Finder在本地删除文件夹时,删除最终会传播到服务器,我需要一个等待服务器更新的测试

为了避免API冲突崩溃,我将我的期望值更改为“弱var”,并将实现它的调用更改为“zeroFoldersExpectation?.fulfill()”并添加了额外的“?”,因为期望值现在是可选的,并且可能变为零,在这种情况下,fulfill调用将被忽略。这修复了崩溃

func testDeleteFolder()
{
    Finder.deleteFolder()

    weak var zeroFoldersExpectation=expectationWithDescription("server has zero folders")
    Server.waitUntilNServerFolders(0, withPrefix: "JC/TestSwiftDrive/", completionHandler: {zeroFoldersExpectation?.fulfill()})
    waitForExpectationsWithTimeout(10, handler: {error in})

}

与创建
expectation
as
weak
变量(如中所建议的)不同,我认为您还可以在
waitForExpectationsWithTimeout
的完成处理程序中设置is
block
变量并将其置为零:

-(void)test1TaskLongerThanTimeout
{
__block XTestExpection*expectation=[自我期望与描述:@“测试1:任务长于超时”];
[自启动同步任务,持续时间:4 completionHandler:^(id结果,N错误*错误){
xctasertnotnil(结果);
xctasertnil(错误);
[期望实现];
}];
[self-waitForExpectationsWithTimeout:2处理程序:^(N错误*错误){
期望=零;
}];
}

通过这种方式,您可以确保ARC不会太快解除锁定
期望值。

如果代码使用MRC而不是ARC,该怎么办?ARC只支持弱引用,因此您必须转换到ARC。使用这种方法,我担心期望值可能会在您使用它之前解除锁定,但如果这对你有用的话,我想这是不会发生的。非常感谢。这是一个非常有用的解决方案,使测试比其他块解决方案更具可读性。谢谢@0xced,这为我节省了很多时间。谢谢。为我工作。我认为如果调用
completionHandler
,您也需要失败
XCTFail(“不应完成”)
并删除回调中的其余代码。是的,您可以这样做。但是,当预期没有及时实现时,测试也应该自动失败。我通常也会在完成处理程序中将错误打印到控制台。它会失败,因为没有调用fulfill。这既可靠又简单。在弱引用的情况下,我观察到超时后期望值并没有为空的情况。这可能取决于期望值如何
func testDeleteFolder()
{
    Finder.deleteFolder()

    weak var zeroFoldersExpectation=expectationWithDescription("server has zero folders")
    Server.waitUntilNServerFolders(0, withPrefix: "JC/TestSwiftDrive/", completionHandler: {zeroFoldersExpectation?.fulfill()})
    waitForExpectationsWithTimeout(10, handler: {error in})

}