【问题标题】:In Xcode 8, what is the way to make test methods run in a particular order within a given XCTestCase class?在 Xcode 8 中,使测试方法在给定 XCTestCase 类中以特定顺序运行的方法是什么?
【发布时间】:2017-01-28 21:15:06
【问题描述】:

在 Xcode 8 之前,您可以通过在给定的 XCTestCase 类中按字母顺序排列测试方法的名称(如this answer 中所述),让您的单元测试以特定顺序运行。例如,测试会像这样运行:testA、testB、testC、testD 等。

但是在 Xcode 8 中,情况不再如此。例如,我有名为 test1、test2、test3、test4 的测试方法,并且 test4 将首先运行(见下面的截图)。然后我可以重新运行,test2 将在下一次运行时首先运行。

那么我现在如何让测试在 Xcode 8 上按顺序运行?

【问题讨论】:

  • 正如您链接到的问答的 cmets 中所说,单元测试应该能够独立于任何其他测试运行 - 为什么需要订购它们?
  • 它们不是单元测试,它们是端到端测试中的顺序测试。为什么它们必须是单元测试?并非所有测试都是这个世界上的单元测试。
  • 不管怎样,我暂时通过使用观察者和选择器、完成块等将它全部变成了一个大测试。你会发现有些情况下大的端到端测试可能非常除了标准的单元测试之外很有价值。但是,如果每个 XCTestCase 方法都应该代表一个“单元”而不依赖于其他测试,那么,它就是这样。我只是不确定这是否是一个错误。
  • 但我们喜欢端到端测试,因为它模拟了真实用户实际使用应用程序的方式,并且在帮助我们找到与多线程相关的棘手问题方面非常宝贵同时,单元测试根本看不到,因为单元是它们自己线程上的东西,而单元测试无法处理其他线程中发生的事情等。
  • 我在同一条船上,需要按特定顺序运行一组已定义的 UI 测试。如果不先处理登录页面,您将无法访问登录页面。与单元测试无关,因为它是端到端的。我现在不知所措,不想创建 10 个测试目标来执行健全性测试。

标签: objective-c xcode unit-testing xcode8 xctestcase


【解决方案1】:

所以我解决这个问题的方法如下。

问题回顾:

我需要连续运行多个测试:test1test2test3test4。每个测试都设置一个期望值,测试的最后一步将满足期望值,然后测试将结束并运行下一个测试。

但是在 Xcode 8 中,测试现在以随机顺序运行。虽然从这样的角度来看这很好,如果它们是单元测试,它们应该能够以随机顺序运行,但如果它们不是作为单元测试而是作为 end 设计的,它会破坏你的测试-to-end 测试。

例如,在我的情况下,它会破坏测试,因为在第一个测试中,用户登录并设置了一些数据等。然后,第二个测试检查数学,然后第三个测试将数据同步到服务器,然后第四个将其全部删除并从服务器同步下来。当第一个测试运行时,在构建时,一个 shell 脚本从一个 MSYQL 文件初始化服务器的数据库,然后在应用程序启动时,AppDelegate 为应用程序安装一个新的核心数据数据库。因此,如果我必须在每次测试后重新启动应用程序,shell 脚本将重新初始化服务器数据库并导致应用程序的本地核心数据数据库也重新初始化。这将破坏后续测试(这是一个端到端测试,后续测试取决于应用程序和服务器的状态,在之前的测试运行之后)。

而不是设置四个不同的 Core Data 启动数据库和四个不同的服务器初始化脚本(这将是一个巨大的痛苦,并且每当我们有架构更改时,端到端测试的管理时间会成倍增加),或者必须记住在运行时无需连续手动构建每个测试,而是使用以下策略将所有四个测试方法合并为一个非常长的测试方法。

解决方案

首先,在 XCTestCase 类中,我设置了一个测试期望属性:

@property (nonatomic, strong) XCTestExpectation *endOfTestExpectation;

在我的 XCTestCase 类中 test1 的末尾,我用这个新的期望替换了它现有的期望,如下所示:

self.endOfTestExpectation = [self expectationWithDescription:
                                  @"endOfTestExpectation"];

[self waitForExpectationsWithTimeout:900 
                             handler:^(NSError * _Nullable error) {
    /* Code moved from test4's expectation completion block goes here */
}

对于每个test1test3,我将测试的原始期望完成块内的代码移动到一个名为completion1completion3 的新方法中。对于test4,我将其原始期望完成块内的代码移动到test1 方法末尾的endOfTestExpectation 完成块中。

然后我将方法 test2test4 重命名为 t3st2t3st4 (我知道,这又快又脏;你应该在它工作后选择更具描述性的东西)。在completion1方法的最后,我调用t3st2;在completion2 结尾我打电话给t3st3;在completion3 结尾我打电话给t3st4;在completion4 结束时,我打电话给[self.endOfTestExpectation fulfill];

这实际上比旧方式更好,因为在旧方式中,即使第一次测试失败,后续测试仍然会运行!现在,无论XCTFail 发生在哪里,整个事情都会停止,如果我被选中,我们不会浪费时间运行其余部分:D

【讨论】:

  • 感谢您对此进行调查。你会碰巧知道如何在 Swift 中做到这一点吗?
  • 您可以在覆盖 func setUp() 中使用 continueAfterFailure = true 的旧方法,但无论如何,现在看来我必须使用 expect() :( 更多代码
猜你喜欢
  • 2016-08-06
  • 2011-04-11
  • 2021-09-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-13
  • 1970-01-01
  • 1970-01-01
  • 2011-09-09
相关资源
最近更新 更多