Тесты случайным образом выходят из строя с помощью waitForExpectation
, всегда, если я выполняю ожидание в dispatch_after
.
Это происходит как в objective-c, так и в swift.
Полностью случайным образом, иногда это иногда срабатывает. Кто-нибудь есть идеи? (Я запускаю его с помощью CMD + U на MacMini, но я также пробовал с 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)
}
И backtrace
(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)
У меня возникли аналогичные проблемы, и мне потребовалось три дня.
В документации -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.
Скорее всего, ваш NSInputStream
или NSOutputStream
пытается повлиять на runloop во время ожидания, что приведет к сбою.
У Swift есть особые проблемы, чтобы показать правильную точку исключения исключения, когда закрытие присутствует в той же области.
Я видел ту же проблему в XCTestCase, которая использовала dispatch_after
, и даже через контрольную точку исключения была та же строка, что и waitForExpectationsWithTimeout
, тестовый случай терпел крах из-за downcast на объекте nil.
Я знаю, что это не ваше дело, но всякий раз, когда это случается, я предлагаю удалять утверждения по одной строке за раз и запускать тест после каждого удаления. Если тест не сбой, вы определили виновника. Это, к сожалению, самый лучший вариант на момент написания этой статьи, когда Swift показывает контрольную точку исключения на строке, которая не имеет смысла, особенно печально известная строка 0 класса, которую вы можете увидеть в инструментах отчетов о сбоях.
Сообщите нам, если вы выяснили свою аварию.
У меня была такая же проблема. Исправление для меня, по-видимому, увеличивало интервал времени ожидания. Первоначально у меня был интервал ожидания 1 и увеличился до 5, чтобы быть в безопасности. Кажется, сейчас работает нормально. Я не уверен, сколько времени займет ваш асинхронный вызов, или если вы можете позволить себе более длинный интервал времени ожидания, чем 1, но это стоит того.
dispatch_after
который ждет0.5s
. В конце я обнуляю ожидание после блока ожидания и делаю все утверждения также после блока ожидания. Так что ничего не происходит во время ожидания, но выполнить.