iOS 测试因 waitForExpectationsWithTimeout 而崩溃

Posted

技术标签:

【中文标题】iOS 测试因 waitForExpectationsWithTimeout 而崩溃【英文标题】:iOS Test crashes with waitForExpectationsWithTimeout 【发布时间】:2015-01-15 19:17:04 【问题描述】:

如果我满足dispatch_after 的期望,测试会随机崩溃waitForExpectation

它发生在objective-c 和swift 中。

完全随机,有时有效,有时无效。有人有什么主意吗? (我在 MacMini 上使用 CMD+U 运行它,但我也尝试使用 MBP Retina,结果相同)

示例代码:

func testBarcodeNotFound() 
        let exp = self.expectationWithDescription("store loading")
        OHHTTPStubs.stubRequestsPassingTest( (request:NSURLRequest!) -> Bool in
            if request.URL.absoluteString == nil 
                return false
            
            return request.URL.absoluteString!.hasSuffix("/products/barcode/1422/")
            , withStubResponse:  (request:NSURLRequest!) -> OHHTTPStubsResponse! in
                let data = "".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
                return OHHTTPStubsResponse(data: data, statusCode: 404, headers: ["Content-Type":"application/json"])

        )
        self.productsVC.scanningVC.successScan("1422", "EAN13")
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue())  () -> Void in
            exp.fulfill()
        

        self.waitForExpectationsWithTimeout(1, handler: nil)

        XCTAssertTrue(self.delegateForProductsVC.barcodeNotFoundCalled)

    

回溯

(lldb) bt
* thread #1: tid = 0x27e405, 0x06461ab0 libdispatch.dylib`_dispatch_semaphore_dispose + 92, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x06461ab0 libdispatch.dylib`_dispatch_semaphore_dispose + 92
    frame #1: 0x06463578 libdispatch.dylib`_dispatch_dispose + 43
    frame #2: 0x064759e1 libdispatch.dylib`_os_object_dispose + 33
    frame #3: 0x06475cb1 libdispatch.dylib`-[OS_dispatch_object _xref_dispose] + 58
    frame #4: 0x064759bb libdispatch.dylib`_os_object_xref_dispose + 33
    frame #5: 0x05759eb1 libobjc.A.dylib`objc_release + 65
    frame #6: 0x2011949d XCTest`__destroy_helper_block_95 + 29
    frame #7: 0x064e4793 libsystem_sim_blocks.dylib`_Block_release + 211
    frame #8: 0x0647603f libdispatch.dylib`_dispatch_client_callout + 14
    frame #9: 0x0645f764 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 470
    frame #10: 0x05af095e CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 14
    frame #11: 0x05aaf760 CoreFoundation`__CFRunLoopRun + 2256
    frame #12: 0x05aaebcb CoreFoundation`CFRunLoopRunSpecific + 443
    frame #13: 0x05aae9fb CoreFoundation`CFRunLoopRunInMode + 123
    frame #14: 0x04c2ed98 Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 309
    frame #15: 0x20119a31 XCTest`-[XCTestCase(AsynchronousTesting) waitForExpectationsWithTimeout:handler:] + 1192
  * frame #16: 0x1055be0b SomeAppNative`SomeApp.ProductsDelegatesViewControllerTest.testBarcodeNotFound (self=0x7b0dbdf0)() -> () + 5595 at ProductsDelegatesViewControllerTest.swift:108
    frame #17: 0x1055c1a2 SomeAppNative`@objc SomeApp.ProductsDelegatesViewControllerTest.testBarcodeNotFound (SomeApp.ProductsDelegatesViewControllerTest)() -> () + 34 at ProductsDelegatesViewControllerTest.swift:0
    frame #18: 0x05a7976d CoreFoundation`__invoking___ + 29
    frame #19: 0x05a79618 CoreFoundation`-[NSInvocation invoke] + 360
    frame #20: 0x2010897b XCTest`-[XCTestCase invokeTest] + 320
    frame #21: 0x20108bb9 XCTest`-[XCTestCase performTest:] + 184
    frame #22: 0x20114162 XCTest`-[XCTest run] + 314
    frame #23: 0x20107598 XCTest`-[XCTestSuite performTest:] + 406
    frame #24: 0x20114162 XCTest`-[XCTest run] + 314
    frame #25: 0x20107598 XCTest`-[XCTestSuite performTest:] + 406
    frame #26: 0x20114162 XCTest`-[XCTest run] + 314
    frame #27: 0x20107598 XCTest`-[XCTestSuite performTest:] + 406
    frame #28: 0x20114162 XCTest`-[XCTest run] + 314
    frame #29: 0x20103de2 XCTest`__25-[XCTestDriver _runSuite]_block_invoke + 61
    frame #30: 0x20110c82 XCTest`-[XCTestObservationCenter _observeTestExecutionForBlock:] + 184
    frame #31: 0x20103d06 XCTest`-[XCTestDriver _runSuite] + 285
    frame #32: 0x20104951 XCTest`-[XCTestDriver _checkForTestManager] + 272
    frame #33: 0x20104c6b XCTest`-[XCTestDriver runTestSuite:completionHandler:] + 378
    frame #34: 0x2011775c XCTest`+[XCTestProbe runTests:] + 216
    frame #35: 0x04c2ab57 Foundation`__NSFireDelayedPerform + 423
    frame #36: 0x05af08d6 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
    frame #37: 0x05af025d CoreFoundation`__CFRunLoopDoTimer + 1309
    frame #38: 0x05aaf6ba CoreFoundation`__CFRunLoopRun + 2090
    frame #39: 0x05aaebcb CoreFoundation`CFRunLoopRunSpecific + 443
    frame #40: 0x05aae9fb CoreFoundation`CFRunLoopRunInMode + 123
    frame #41: 0x0712e24f GraphicsServices`GSEventRunModal + 192
    frame #42: 0x0712e08c GraphicsServices`GSEventRun + 104
    frame #43: 0x034518b6 UIKit`UIApplicationMain + 1526
    frame #44: 0x0053dae5 SomeApp`main(argc=16, argv=0xbff6e4a4) + 213 at main.m:16
    frame #45: 0x0649cac9 libdyld.dylib`start + 1
(lldb) 

【问题讨论】:

【参考方案1】:

我遇到了类似的问题,我花了三天时间才弄清楚。

-waitForExpectationsWithTimeout:handler: 的文档说:

 * -waitForExpectationsWithTimeout:handler: runs the run loop while handling events until all expectations
 * are fulfilled or the timeout is reached. Clients should not manipulate the run
 * loop while using this API.

很可能您的NSInputStreamNSOutputStream 在等待期间试图影响运行循环,这将导致崩溃。

【讨论】:

【参考方案2】:

我有同样的问题。对我来说,修复似乎是增加超时间隔。为了安全起见,我最初的超时间隔为 1 并将其增加到 5。现在看来工作正常。我不确定您的异步调用需要多长时间,或者您是否可以承受比 1 更长的超时间隔,但值得一试。

【讨论】:

我试过这个,在 50% 的情况下它有帮助。您可以在我发布的代码中看到异步只是等待0.5sdispatch_after。最后,我在等待块之后消除了期望,并且也在等待块之后执行了所有断言。所以在等待时什么都不会发生,而是完成。【参考方案3】:

当闭包存在于同一范围内时,Swift 在显示正确的异常断点方面遇到了特别的麻烦。

我在使用 dispatch_after 的 XCTestCase 中看到了同样的问题,即使异常断点与 waitForExpectationsWithTimeout 在同一行,测试用例也会因为对 nil 对象的向下转换而崩溃。

我知道这不是你的情况,但每当发生这种情况时,我建议一次删除一行语句并在每次删除后运行测试。如果测试没有崩溃,那么您已经确定了罪魁祸首。不幸的是,在撰写本文时,当 Swift 在没有意义的行上显示异常断点时,这是最好的选择,尤其是您可能在崩溃报告工具中看到的类的臭名昭著的第 0 行。

如果您已经解决了崩溃问题,请告诉我们。

【讨论】:

以上是关于iOS 测试因 waitForExpectationsWithTimeout 而崩溃的主要内容,如果未能解决你的问题,请参考以下文章

iOS:浏览器因内存不足而崩溃

测试飞行应用程序因 libAVFAudio.dylib 崩溃:AVAE_RaiseException(NSString*, ...) + 60

React Native / Expo iOs - 网络请求因我的 Api 上的 Fetch 而失败

iOS - 如果仅在设备上运行,我的应用程序会因内存错误而崩溃

IOS - 上APPSTORE为何因IPv6被拒?

IOS - 上APPSTORE为何因IPv6被拒?