我在很多测试中使用XCTestExpectation
,有时(非常随机)某些期望未得到满足(尽管我确信它们应该是)。
在调查这个问题的同时,我注意到在主线程中满足了一些期望,并且在后台线程中实现了一些期望。到目前为止,这些问题都与后台线程中遇到的问题有关。
从后台线程满足期望是否安全?我找不到任何关于此的明确信息。
以下是我如何使用XCTestExpectation
:
__block XCTestExpectation *expectation = [self expectationWithDescription:@"test"];
[self doSomethingAsyncInBackgroundWithSuccess:^{
[expectation fullfill];
}];
[self waitForExpectationsWithTimeout:10.0 handler:^(NSError *error) {
expectation = nil;
if (error) {
NSLog(@"Timeout Error: %@", error);
}
}];
答案 0 :(得分:4)
在XCTestExpectation是线程安全的任何地方都没有记录。由于没有关于此事的官方文档,您只能通过创建测试示例来猜测:
- (void)testExpectationMainThread;
{
__block XCTestExpectation *expectation = [self expectationWithDescription:@"test"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
NSLog(@"%@", error);
}];
}
- (void)testExpectationStartMainThreadFulfilBackgroundThread;
{
__block XCTestExpectation *expectation = [self expectationWithDescription:@"test"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, kNilOptions), ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
NSLog(@"%@", error);
}];
}
- (void)testExpectationBackgroundThread;
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, kNilOptions);
__block XCTestExpectation *expectation;
dispatch_sync(queue, ^{
expectation = [self expectationWithDescription:@"test"];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
NSLog(@"%@", error);
}];
}
此处它不会崩溃或导致问题,但由于缺乏官方文档,坚持使用相同的队列可能更安全。
你应该真正隐藏方法doSomethingAsyncInBackgroundWithSuccess
并为应用提供本地"虚拟"数据
您的单元测试不应该依赖于网络,因为它是可变的。
您应该在主线程上执行doSomethingAsyncInBackgroundWithSuccess
的完成块(或者至少提供一种在同一线程上一致回调的方法),您可以使用GCD轻松完成此操作。
- (void)doSomethingAsyncInBackgroundWithSuccess:(void (^)(void))completion;
{
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
或使用NSOperationQueue mainQueue
- (void)doSomethingAsyncInBackgroundWithSuccess:(void (^)(void))completion;
{
[NSOperationQueue.mainQueue addOperationWithBlock:^{
completion();
}];
}