在Xcode 9 GM

时间:2017-09-19 20:50:16

标签: ios xcode xctest xcode9-beta xcode9

我在Xcode 9 GM上运行我的iOS应用程序的单元测试,其中有几个因为奇怪的NSInternalInconsistencyException而失败,抱怨无法报告某些测试断言,因为所涉及的测试没有关联的XCTestRun对象。我正在使用OCMockito + OCHamcrest进行模拟和呼叫验证。

出于演示目的,假设我的应用程序名为MyTestApp,我有一个测试类FooTest(继承自XCTestCase)。在-setUp中,我为测试创建并连接各种模拟对象,而在-tearDown中我将它们设置为nil以防万一。

以下是我得到的例外情况的一个例子:

2017-09-19 13:23:01.852729-0700 xctest[17006:5392130] *** Assertion failure in -[FooTest recordFailureWithDescription:inFile:atLine:expected:], /Library/Caches/com.apple.xbs/Sources/XCTest_Sim/XCTest-13201/Sources/XCTestFramework/Core/XCTestCase.m:308
/Users/fooUser/Workspaces/MyTestApp/tests/FooTest.mm:46: error: -[FooTest testSomething] : failed: caught "NSInternalInconsistencyException", "Unable to report test assertion failure 'Expected 1 matching invocation, but received 0' from /Users/fooUser/Workspaces/MyTestApp/tests/FooTest.mm:135 because it was raised inside test case -[FooTest testSomethingElse] which has no associated XCTestRun object. This may happen when test cases are constructed and invoked independently of standard XCTest infrastructure."
(
    0   CoreFoundation                      0x000000010255d1cb __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x0000000101ebff41 objc_exception_throw + 48
    2   CoreFoundation                      0x0000000102562362 +[NSException raise:format:arguments:] + 98
    3   Foundation                          0x0000000101827089 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 193
    4   XCTest                              0x0000000101d96875 -[XCTestCase recordFailureWithDescription:inFile:atLine:expected:] + 518
    5   MyAppUnitTests                     0x000000011d7f2f5a -[HCXCTestFailureReporter executeHandlingOfFailure:] + 154
    6   MyAppUnitTests                     0x000000011d7f2b73 -[HCTestFailureReporter handleFailure:] + 67
    7   MyAppUnitTests                     0x000000011d64ec9a MKTFailTest + 217
    8   MyAppUnitTests                     0x000000011d64c3f4 -[MKTExactTimes verifyData:] + 254
    9   MyAppUnitTests                     0x000000011d64f2ba -[MKTBaseMockObject verifyInvocation:usingVerificationMode:] + 137
    10  MyAppUnitTests                     0x000000011d64f20b -[MKTBaseMockObject handlingVerifyOfInvocation:] + 115
    11  MyAppUnitTests                     0x000000011d64f15a -[MKTBaseMockObject forwardInvocation:] + 64
    12  CoreFoundation                      0x00000001024dfed8 ___forwarding___ + 760
    13  CoreFoundation                      0x00000001024dfb58 _CF_forwarding_prep_0 + 120
    14  MyAppUnitTests                     0x000000011c40b486 -[FooTest setUp] + 294
    15  XCTest                              0x0000000101d97b39 __24-[XCTestCase invokeTest]_block_invoke_3 + 31
    16  XCTest                              0x0000000101d97809 __24-[XCTestCase invokeTest]_block_invoke + 271
    17  XCTest                              0x0000000101ddff45 -[XCUITestContext performInScope:] + 183
    18  XCTest                              0x0000000101d976ef -[XCTestCase invokeTest] + 141

有几点需要注意:

  • 单元测试不是在XCTest框架之外初始化XCTestCase或任何XCTest“基础结构”类。
  • 受影响的单元测试的其余语句实际执行,直到异常点。
  • 所涉及的代码行如下所示:[verify(_mockTestObject) description];
  • 堆栈跟踪是关于问题发生的地方 - 它表示它在-setUp中,但据我所知,OCMockito只是报告 next 单元测试运行。问题位置(FooTest.mm:135)看起来像上面的verify()调用。

事实上,所有因此异常而失败的单元测试都会失败,因为我们正在尝试验证是否已在一个模拟对象上调用-[NSObject description]。删除验证该调用的所有实例都会使测试通过。

我在Google上搜索了此特定NSInternalInconsistencyException的其他实例,但没有产生任何结果。我不确定以什么方式 - [NSObject描述]与可能在模拟对象上调用的任何其他方法不同 - 问题可能不存在,但它只是表现出来。我也试图找到是否有任何调试/诊断选项我可以为XCTest打开,所以我可能会得到更多关于测试执行的详细日志信息,但我也没有找到类似的东西。

我接下来应该关注的任何想法?非常感谢!

2 个答案:

答案 0 :(得分:2)

我在处理一些大型遗留代码测试时遇到了类似的问题。问题是:当您单独运行时,XCTExpectation的测试是否正常?如果是这样,这意味着在使用NSInternalInconsistencyException进行测试之前执行的某些测试是以相关测试完成后执行它们的方式调度XCTest相关方法。

看起来像(例子):

Test1 - >异步调度执行XCTFail

的“阻止”

Test1 - >饰面

XCTFail在主线程或其他线程上执行(但Test1通过,因为它完成时没有“失败”)。

Test2 - >使用XCTExpectation - >测试内容NSInternalInconsistencyException

Apple文档没有提供有关XCTest内部内容的大量信息,但我很确定这是问题所在。尝试按照以下故障排除步骤确定测试“冲突”(在没有XCTestExpectation的情况下执行异步操作的错误,比如使用完成处理程序的方法,最终执行XCTest断言,失败等)。

  1. 在套件中执行二进制搜索:停用一半测试并使用FooTest运行套件。
  2. 如果你的测试套件运行良好,重新启用一半的禁用测试。如果测试套件运行异常,则重新启用所有禁用的测试并禁用另一半。
  3. 重复步骤1,然后再进行少量测试。
  4. 最后,您将最终得到导致此异常的测试。
  5. 在我的情况下,有多个测试导致与XCTestExpectation的冲突,因此搜索非常麻烦(1000 + XCTestCases套件需要几个小时,所以大约5k测试)。

    然后彻底调查测试中发生的与您的测试相冲突的事情。

答案 1 :(得分:0)

可能是将expectation.expectedFulfillmentCount设置为较低的数字。

expectation.expectedFulfillmentCount = numberOfTimesCalled

您的numberOfTimesCalled可能低于期望值被调用的次数。例如,您可能打了两次电话,但是期望值被设置为一次。