XCTest:由于未捕获的异常 'NSInternalInconsistencyException' 而终止应用程序,原因:'Parameter "test" must not be nil.'

XCTest: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Parameter "test" must not be nil.'

在 运行 某些单元测试时出现此错误不一致:

2018-12-20 09:11:34.892 MyApp[4530:106103] * Assertion failure in void _XCTFailureHandler(XCTestCase *__strong _Nonnull, BOOL, const char * _Nonnull, NSUInteger, NSString *__strong _Nonnull, NSString *__strong _Nullable, ...)(), /Library/Caches/com.apple.xbs/Sources/XCTest_Sim/XCTest-14460.20/Sources/XCTestFramework/Core/XCTestAssertionsImpl.m:41 2018-12-20 09:11:34.929 MyApp[4530:106103] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Parameter "test" must not be nil.'

似乎某些断言失败了,因为参数是 nil,但我很难确定是哪一个。

环境: Xcode 10.1 iOS申请

XCTest 测试后评估的断言 "finishes" 如果断言失败,将抛出此异常:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Parameter "test" must not be nil.'

证明这一点的基本示例是以下测试:

func testRaceCondition() {
    DispatchQueue.main.async {
        XCTAssertEqual(1 + 1, 3) // Assertion fails and 'nil' exception is thrown 
    }
}

断言是运行异步的,但测试不等待异步块完成。因此,在评估断言时,测试已经完成并且测试用例已经发布(因此是 nil)。

恐怖

如果断言通过,上述代码将不会抛出任何错误。下面的代码似乎通过了测试,但从失败将抛出上述异常而不是正确地使测试失败的意义上说,这是危险的:

func testRaceCondition() {
    DispatchQueue.main.async {
        XCTAssertEqual(1 + 1, 2) // Assertion passes 
    }
}

解决方案

为防止出现此问题,所有在异步执行的块中评估断言的测试都应使用预期并等待它们完成:

func testRaceCondition() {
    let asyncExpectation = expectation(description: "Async block executed")
    DispatchQueue.main.async {
        XCTAssertEqual(1 + 1, 3)
        asyncExpectation.fulfill()
    }
    waitForExpectations(timeout: 1, handler: nil)
}

通过使用期望,我们将得到正确的失败测试错误,而不是上面发布的难以调试的异常:

XCTAssertEqual failed: ("2") is not equal to ("3")