Xcode测试隔离通过,与其他测试一起运行时失败

问题描述 投票:14回答:3

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

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

其他测试失败,但是将具有相同URL的请求粘贴到浏览器时会返回适当的数据。

我的网络代码封装在NSOperation中运行的NSOperationQueue对象中。 (我的操作队列是默认类型-我尚未将底层GCD队列显式设置为串行或并发。)

我该如何看待这些测试?阅读this post on objc.io之后,我假设它们正在遭受某种隔离问题。

ios xcode unit-testing nsoperation xctest
3个回答
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();
  }];
}  

Perform块将被建立,但是您不会等待它完成,因为您实际上并没有在等待内部块,而只是在等待外部块。关键是XCTestWaits实际上让测试完成,然后仅检查在一定时间内是否已实现了承诺,但与此同时它将开始运行其他测试。该success()可以出现在任意数量的位置,并且可以产生任意数量的怪异行为。

隔离行为(而不是隔离)来自以下事实:如果仅运行此测试,由于运气,一切都会好起来;但是,如果运行多个测试,则CoreData块可能会一直停留到下一个异步测试为止,然后它将“解除阻止”其执行,并且它将在某个随机的将来时间开始执行,以进行一些随机的未来测试。

短期的明确修改是暂停测试,直到一切完成。这是一个例子:

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
投票

在与NSOperationQueue战斗并且从waitUntilAllOperationsAreFinished看似不正确的返回几天后,我遇到了一个更简单的选择:将测试分为多个测试目标。这为您的测试提供了自己的“应用程序”环境,更重要的是,在这种情况下,确保Xcode / XCUnit可以按顺序运行它们,以使它们不会相互干扰-除非它们进行了使数据库变脏的工作(可能应该无论如何还是失败)。

最快的方法是复制测试目标,从原始目标中删除失败的测试,并从新目标中删除所有失败的测试(除了失败的测试之外)。请注意,如果您有多个相互干扰的测试,则许多都需要多个目标才能实现足够的隔离。

<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLnN0YWNrLmltZ3VyLmNvbS9QYk9oSi5wbmcifQ==” alt =“额外目标”>

您可以通过检查测试目标方案来检查测试是否已执行。在方案中,您应该同时看到两个(全部)测试目标,并在其下方分别进行测试。

“方案编辑器”


0
投票

[我在使用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)

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

© www.soinside.com 2019 - 2024. All rights reserved.