Unit test Asynchronous wait failed: Exceeded timeout Swift

Viewed 8920

I am trying to paerform a unit test on my application and majority of the test failed and the reason it says is Asynchronous wait failed: Exceeded timeout of 30 seconds, with unfulfilled expectations: "Home Code".

I do not know why it fails like this but this is my code below

class HomeTest: XCTestCase {

    override func setUp() {
    }

    override func tearDown() {
    }

    func testHome() {
        let expec = expectation(description: "Home Code")
        let presenter =  HomePresenter(view: HomeTestVC(expectation: expec), source: Repository.instance)
        presenter.start()
        wait(for: [expec], timeout: 30)
    }

    func testPerformanceExample() {
        self.measure {
        }
    }

}


class HomeTestVC: HomeContract.View {
    func showRatingForLastTrip(_ trip: Trip) {}

    func setProgress(enabled: Bool) {}

    func didFail(message: String) {}

    func didShowError(error: Error) {}

    func didShowStatusCode(code: Int?) {
        XCTAssertGreaterThan(200, code ?? 0)
        self.expec.fulfill()
    }

    var expec: XCTestExpectation
    init(expectation: XCTestExpectation) {
        self.expec = expectation
    }
} 

It pops up the simulator but stays on just the first screen. I do not know why. Any help would be appreciated

3 Answers

You are not fulfilling your expectations

func testExample() {
    let expec = expectation(description: "Home Code")
    someAsyncTask() { _ in 
       expectation.fulfill()
    }
    wait(for: [expec], timeout: 30)
}

See Testing Asynchronous Operations with Expectations

Notes:

  • Don't pass unit test specific code to production code like you are currently.
  • Loading a VC is not an async task. so shouldn't need an expectation
  • You probably shouldn't be loading the Home class directly, especially if it does trigger some async task. You should look at testing the async parts seperately and using Mocks/Stubs

waitForExpectations() should come LAST

let exp = expectation(description: "wait for exp.fulfill()")

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    XCTAssertTrue(true)
    
    exp.fulfill()
}

waitForExpectations(timeout: 2) // make sure this comes after your assertions

Note: I had attempted to use sleep(1) to create a one second delay, but that doesn't really work with how Xcode tests are set up. Just use the dispatch queue asyncAfter() method if you need a delay, and make sure waitForExpectations() is called very last.

You need to fulfill the expectation. Like this:

let expectation = self.expectation(description: "Alert")

DispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: {

    expectation.fulfill()
})

waitForExpectations(timeout: 5, handler: nil)

XCTAssert(whatever)

Related