所以我解决这个问题的方法如下。
问题回顾:
我需要连续运行几个测试:test1
, test2
, test3
, test4
. 每个测试都会建立一个期望,测试的最后一步将满足期望,然后测试将结束,下一个测试将运行。
但是在 Xcode 8 中,测试现在以随机顺序运行。虽然从这样的角度来看这很好,如果它们是单元测试,它们应该能够以随机顺序运行,但如果它们不是作为单元测试而是作为端到端测试设计的,它就会破坏你的测试。
例如,在我的情况下,它破坏了测试,因为在第一个测试中,用户登录并设置了一些数据等。然后,第二个测试检查数学,然后第三个测试将数据同步到服务器,然后是第四个将其全部删除并从服务器同步。当第一个测试运行时,在构建时,一个 shell 脚本从一个 MSYQL 文件初始化服务器的数据库,然后在应用程序启动时,AppDelegate 为应用程序安装一个新的核心数据数据库。因此,如果我必须在每次测试后重新启动应用程序,shell 脚本将重新初始化服务器数据库并导致应用程序的本地核心数据数据库也重新初始化。这将破坏后续测试(这是一个端到端测试,后续测试取决于应用程序和服务器的状态,在之前的测试运行之后)。
而不是设置四个不同的核心数据启动数据库和四个不同的服务器初始化脚本(这将是一个巨大的痛苦,并且每当我们有模式更改时,端到端测试的管理时间会成倍增加),或者必须请记住不要连续手动构建每个测试来运行,而是使用以下策略将所有四种测试方法合并为一个非常长的测试方法。
解决方案
首先,在 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 */
}
对于每个test1
through test3
,我将测试的原始期望完成块内的代码移动到一个名为completion1
through的新方法completion3
中。因为test4
我将其原始期望完成块内的代码移动到方法endOfTestExpectation
末尾的 ' 完成块中test1
。
然后我将方法重命名为test2
throughtest4
以命名为t3st2
through t3st4
(我知道,快速而肮脏;你应该在它工作后选择更具描述性的东西)。在completion1
方法结束时,我调用t3st2
; 在completion2
我打电话的时候t3st3
;在completion3
我打电话的时候t3st4
;最后completion4
我打电话[self.endOfTestExpectation fulfill];
。
这实际上最终比旧方式更好,因为在旧方式中,即使第一次测试失败,后续测试仍然会运行!现在,无论XCTFail
发生什么,整个事情都会停止,如果我被标记到 SO 中,我们不会浪费时间运行其余部分:D