我正在使用Xcode 6的新异步测试功能.当异步任务在超时之前结束时,一切正常.但是如果任务花费的时间比超时时间长,事情会变得更复杂.
以下是我正在进行的测试:
@interface AsyncTestCase : XCTestCase @end
@implementation AsyncTestCase
// The asynchronous task would obviously be more complex in a real world scenario.
- (void) startAsynchronousTaskWithDuration:(NSTimeInterval)duration completionHandler:(void (^)(id result, NSError *error))completionHandler
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
completionHandler([NSObject new], nil);
});
}
- (void) test1TaskLongerThanTimeout
{
XCTestExpectation *expectation = [self expectationWithDescription:@"Test 1: task longer than timeout"];
[self startAsynchronousTaskWithDuration:4 completionHandler:^(id result, NSError *error) {
XCTAssertNotNil(result);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:2 handler:nil];
}
- (void) test2TaskShorterThanTimeout
{
XCTestExpectation *expectation = [self expectationWithDescription:@"Test 2: task shorter than timeout"];
[self startAsynchronousTaskWithDuration:5 completionHandler:^(id result, NSError *error) {
XCTAssertNotNil(result);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:nil];
}
@end
Run Code Online (Sandbox Code Playgroud)
不幸的是,fulfill在超时过期后调用该方法会使测试套件崩溃并出现以下错误:
API违规 - 在等待上下文结束后调用 - [XCTestExpectation fulfill].
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'API violation - called -[XCTestExpectation fulfill] after the wait context has ended.'
*** First throw call stack:
(
0 CoreFoundation 0x000000010c3a6f35 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010a760bb7 objc_exception_throw + 45
2 CoreFoundation 0x000000010c3a6d9a +[NSException raise:format:arguments:] + 106
3 Foundation 0x000000010a37d5df -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
4 XCTest 0x0000000115c48ee1 -[XCTestExpectation fulfill] + 264
...
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Run Code Online (Sandbox Code Playgroud)
当然我可以在调用这样的fulfill方法之前检查测试是否完成:
- (void) test1TaskLongerThanTimeout
{
XCTestExpectation *expectation = [self expectationWithDescription:@"Test 1: task longer than timeout"];
__block BOOL testIsFinished = NO;
[self startAsynchronousTaskWithDuration:4 completionHandler:^(id result, NSError *error) {
if (testIsFinished) {
return;
}
XCTAssertNotNil(result);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:2 handler:^(NSError *error) {
testIsFinished = YES;
}];
}
Run Code Online (Sandbox Code Playgroud)
但这似乎过于复杂,使得测试更难以阅读.我错过了什么吗?有没有更简单的方法来解决这个问题?
0xc*_*ced 45
是的,有一种更简单的方法可以避免此API违规问题:只需将期望变量声明为__weak.虽然没有明确记录,但超时到期时会发布预期.因此,如果任务花费的时间超过超时,则在调用任务完成处理程序时,期望变量将为nil.因此,该fulfill方法将被调用为零,什么也不做.
- (void) test1TaskLongerThanTimeout
{
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"Test 1: task longer than timeout"];
[self startAsynchronousTaskWithDuration:4 completionHandler:^(id result, NSError *error) {
XCTAssertNotNil(result);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:2 handler:nil];
}
Run Code Online (Sandbox Code Playgroud)
小智 12
我遇到了同样的问题,但在我的情况下,我需要上面的答案的Swift版本.
我正在为OSX开发OpenStack Swift Drive.当使用Finder在本地删除文件夹时,删除最终会传播到服务器,我需要一个等待服务器更新的测试.
为了避免API违规崩溃,我已经将我的期望改为"弱变量",并将调用更改为"zeroFoldersExpectation?.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})
}
Run Code Online (Sandbox Code Playgroud)
而不是创造的expectation作为weak变量(如建议这个答案),我想你也可以将它作为block变量,零它的完成处理程序waitForExpectationsWithTimeout:
- (void) test1TaskLongerThanTimeout
{
__block XCTestExpectation *expectation = [self expectationWithDescription:@"Test 1: task longer than timeout"];
[self startAsynchronousTaskWithDuration:4 completionHandler:^(id result, NSError *error) {
XCTAssertNotNil(result);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:2 handler:^(NSError *error) {
expectation = nil;
}];
}
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以确保ARC不会expectation快速释放.