Search code examples
xcodeswiftasynchronousosx-elcapitan

Xcode 7/Swift 2.0 XCTestCase waitForExpectationsWithTimeout() EXC_BAD_ACCESS


I have had little success practicing test driven development with asynchronous Swift 2.0 code in Xcode 7. The only solutions with which I have had any success are contrived and hacky delay mechanisms that sidestep the need for waitForExepectationsWithTimeout(). I would like to perform asynchronous tests as follows, but this code consistently fails:

import Foundation
import XCTest

class AsyncTest : XCTestCase {
    func testAsync() {
        let expectation : XCTestExpectation = self.expectationWithDescription("That waitForExpectationsWithTimeout() will actually wait.")
        let queue : dispatch_queue_t = dispatch_queue_create("async", nil);
        dispatch_async(queue, { () -> Void in
            print("Executed!")
            XCTAssert(true, "An aphorism about success.")
            expectation.fulfill()
        })
        waitForExpectationsWithTimeout(100.0, handler: nil) // Arbitrary wait period of 100
    }
}

Error:

Thread 1: EXC_BAD_ACCESS(code=1, address=0x6.....)

When the expectation is fulfilled (expectation.fulfill()) outside of the asynchronously executed closure, this test will pass as expected (so long as I comment out the fulfillment inside the closure). But doing so obviously defeats the purpose of synchronizing test evaluation.

I will note that even though the test fails, the Executed! message prints as one would expect. Also, if a breakpoint is introduced on the waitForExpectationsWithTimeout... line, the test succeeds—similarly, the test succeeds when an artificial sleep delay is introduced. This leads me to believe that waitForExepectaionsWithTimeout() is not waiting at all.

Admittedly I am new to Xcode and Swift, so if I am missing something obvious, I would greatly appreciate any feedback. What is wrong with my above code? Are there any environmental variables that I can provide to help debug the problem?

Running: OS X El Capitan 10.11 Beta (15A263e), Xcode 7.0 beta (7A120f)


Solution

  • I have been having the same problem and could not figure out how to make it work. I made sure my expectations are still allocated but it still failed. So I moved to a different patter, by using a dispatch group. See the example bellow with your code:

    func testSomething(){
        let group = dispatch_group_create()
        dispatch_group_enter(group)
    
        let queue : dispatch_queue_t = dispatch_queue_create("async", nil);
        dispatch_async(queue, { () -> Void in
            print("Executed!")
            XCTAssert(true, "An aphorism about success.")
            dispatch_group_leave(group)
        })
    
        let wait_success = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW,Int64(2*NSEC_PER_SEC)))
        XCTAssertEqual(wait_success, 0)
    }
    

    I'd surely rather use the standard waitForExpectation() mechanism, but if all else fails, then this works just fine