Xcode测试单独传递,与其他测试一起运行时失败

时间:2015-01-21 21:17:03

标签: ios xcode unit-testing nsoperation xctest

我已经编写了一些带有XCTest期望的异步单元测试来测试我编写的网络类。我的大多数测试每次都有效。

当我运行整个套件时,有一些测试会失败,但是会自行传递。

其他测试失败,但使用相同的网址发出请求会在粘贴到浏览器时返回相应的数据。

我的网络代码封装在NSOperation上运行的NSOperationQueue个对象中。 (我的操作队列是默认类型 - 我没有明确地将基础GCD队列设置为串行或并发。)

我可以看一下修复这些测试的内容?在阅读this post on objc.io之后,我假设他们正遭受某种隔离问题。

3 个答案:

答案 0 :(得分:8)

你走在正确的道路上。 objc.io文章建议的解决方案可能是正确的方法,但确实需要一些重构。如果你想在进行代码更改狂欢之前将测试作为第一步,那么你可以这样做。

通常,您可以使用XCTestExpectations来执行几乎所有的异步测试。标准模式可能是这样的:

XCTestExpectation *doThingPromise = [self expetationWithDescription:@"Bazingo"];
[SomeService doThingOnSucceed:^{
  [doThingPromise fulfill];
} onFail:^ {
}];
[self waitForExpectationsWithTimeout:1.0 handler:^(NSError *error) {
    expect(error).to.beNil();
}]

如果[SomeService doThingOnSucceed:onFail:]触发异步请求然后直接解析,这样可以正常工作。但是,如果它做了更多奇特的事情,如果:

+ (void)doThingOnSucceed:onFail: {
  [Thing do it];
  [self.context performBlock:^{
    // Uh oh Farfalle-Os
    success();
  }];
}  

执行块将被设置,但你不会等待它完成,因为你实际上并没有等待内部块,只有外部块。关键是XCTestWaits实际上让测试完成,然后只检查承诺是否在一段时间内完成,但同时它将开始运行其他测试。成功()可以出现在任意数量的地方并产生任何数量的奇怪行为。

隔离行为(vs no isolation)来自这样一个事实,即如果你只运行这个测试,一切都可能没问题,但是如果你运行多个测试,那么CoreData块可能会被困住,直到下一个测试为止async,然后将“解锁”其执行,并且它将在未来的某个随机时间开始执行以进行一些随机的未来测试。

短期明确的黑客攻击是暂停你的测试,直到事情结束。这是一个例子:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[SomeService doThingOnComplete:^{
  dispatch_semaphore_signal(semaphore);
}];
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}

这明确阻止测试完成,直到所有内容完成,这意味着在此测试完成之前不会运行任何其他测试。

如果您的测试/代码中存在大量这些情况,我建议您创建一个可在每次测试后等待的调度组的objc.io解决方案。

答案 1 :(得分:1)

在与NSOperationQueue打架waitUntilAllOperationsAreFinished并且看似错误的回复几天后,我找到了一个更简单的选项:将测试划分为多个测试目标。这给你的测试提供了他们自己的'应用'环境,更重要的是在这种情况下,确保Xcode / XCUnit将顺序运行它们以便它们不会相互干扰 - 除非他们做的事情就像让数据库变脏一样(可能应该是无论如何都是失败的。)

执行此操作的最快方法是复制测试目标,从原始测试中删除失败的测试,并从新目标中删除除失败测试之外的所有内容。请注意,如果您有多个测试干扰彼此,则许多测试需要多个目标才能实现足够的隔离。

Extra targets

您可以通过检查测试目标方案来检查测试是否已执行。在该计划中,您应该看到两个(所有)测试目标,并在其下面进行单独的测试。

Scheme editor

答案 2 :(得分:1)

当我使用Publisher运行单元测试时遇到了这个问题,该测试太快地影响了实时API(我将在以后进行模拟),并且速率受限。这是我想出的解决方法。

  1. 在测试类中,我声明了一个DispatchQueue属性,如下所示:
    let testQueue = DispatchQueue(label: "Test Queue", qos: .default)
    let delay = DispatchQueue.SchedulerTimeType.Stride(1.0)

  1. 然后,在每种测试方法中,我在发布者声明之后添加以下内容:
.delay(for: delay, scheduler: testQueue)

我最终忍住了延迟,直到测试通过。