在后台线程上调用XCTestExpectation的方法是否安全?

时间:2016-01-12 15:54:31

标签: ios objective-c unit-testing xctest xctestexpectation

我在很多测试中使用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);
    }
}];

1 个答案:

答案 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();
  }];
}