XCTest参数化测试重试时变量为nil的问题解决咨询
解决XCTest参数化测试重试时变量为nil的问题
问题描述
我通过重写defaultTestSuite实现参数化UI测试,故意让值为2的用例失败。配置测试计划重试失败用例后,重试该用例时self.number变为nil,导致测试因空指针错误失败。如何避免这种情况?
原测试代码
import XCTest final class HelloiOSUITests: XCTestCase { var number: Int? override func setUpWithError() throws { continueAfterFailure = false } func testExample() throws { guard let num = self.number else { XCTFail("Failed with NPE") throw NSError() } print("Running for number: \(num)") if num == 2 { XCTFail("Failing on purpose to demonstrate NPE issue.") } } override class var defaultTestSuite: XCTestSuite { get { let suite = XCTestSuite(name: NSStringFromClass(self)) addTestCase(to: suite, value: 1) addTestCase(to: suite, value: 2) addTestCase(to: suite, value: 3) print("Test method count = \(suite.testCaseCount)") return suite } } static func addTestCase(to suite: XCTestSuite, value: Int) { for invocation in self.testInvocations { let testMethod = HelloiOSUITests(invocation: invocation) testMethod.number = value suite.addTest(testMethod) } } }
问题原因
XCTest在重试失败用例时,会重新创建一个新的HelloiOSUITests实例,但这个新实例不会继承原实例的number属性值——原代码仅在初始创建测试实例时设置number,重试时新实例的number未被赋值,因此变为nil。
解决方法
将测试参数与NSInvocation绑定,利用其userInfo存储参数值,在测试实例的setUp阶段从invocation中取出参数并赋值给number。这样无论重试多少次,新实例都能从关联的invocation中获取正确参数。
修改后的代码
import XCTest final class HelloiOSUITests: XCTestCase { var number: Int? override func setUpWithError() throws { continueAfterFailure = false // 从invocation的userInfo中取出参数赋值给number if let value = invocation.userInfo?["number"] as? Int { self.number = value } } func testExample() throws { guard let num = self.number else { XCTFail("Failed with NPE") throw NSError(domain: "HelloiOSUITests", code: -1) } print("Running for number: \(num)") if num == 2 { XCTFail("Failing on purpose to demonstrate NPE issue.") } } override class var defaultTestSuite: XCTestSuite { get { let suite = XCTestSuite(name: NSStringFromClass(self)) addTestCase(to: suite, value: 1) addTestCase(to: suite, value: 2) addTestCase(to: suite, value: 3) print("Test method count = \(suite.testCaseCount)") return suite } } static func addTestCase(to suite: XCTestSuite, value: Int) { for invocation in self.testInvocations { // 复制invocation,避免修改原实例影响其他测试用例 let copiedInvocation = invocation.copy() as! NSInvocation // 将参数存入invocation的userInfo copiedInvocation.userInfo = ["number": value] let testMethod = HelloiOSUITests(invocation: copiedInvocation) suite.addTest(testMethod) } } }
关键修改点
- 在
addTestCase中,复制原invocation并将参数存入其userInfo,避免修改原invocation导致参数污染 - 在
setUpWithError中,从当前实例的invocation中取出userInfo里的参数,赋值给number - 确保重试时新创建的测试实例能通过关联的
invocation获取到正确的参数值
内容的提问来源于stack exchange,提问作者coldcake71
相关产品推荐
相关产品推荐

