Wait for XCTestExpectation before starting next unit test in XCode

2.3k Views Asked by At

I have a number of asynchronous unit tests which work correctly on their own using expectations. However when I run all the tests in the suit, they do not wait for each other to complete - the asynchronous callbacks are still pending when the next tests start running. What I want is for each test to wait for the expectations in the previous test before running. These tests use a shared database, so having them overlap leads to annoying additional complexity, and failing tests when run as a suite.

- (void)testSignIn {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.

XCTestExpectation *expectation =
[self expectationWithDescription:@"Expectations"];

[_userManager signInWithUsername:kUserEmail andPassword:kUserPassword
                         success:^{
                             XCTAssertNotNil([_userManager getCurrentUser]);

                             XCTAssertNotNil([_userManager getCurrentUser].plan);
                             XCTAssertTrue([_userManager getCurrentUser].plan.liveStream == TRUE);

                             [expectation fulfill];

                         } failure:^(EDApiError *apiError) {
                             XCTAssertTrue(FALSE); // Should not fail
                             [expectation fulfill];

                         }];

[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
    if (error) {
        NSLog(@"Timeout Error: %@", error);
    }
}];

}

2

There are 2 best solutions below

1
On

Use XCTWaiter or waitForExpectations(timeout:handler:) to stall the completion of each test until the expectations have been fulfilled.

This blog post explains some of the more complex pitfalls you might come across when writing asynchronous tests and how to prevent them: https://jeremywsherman.com/blog/2016/03/19/xctestexpectation-gotchas/

0
On

I've found you need to fulfil expectations on the main thread - important if your async completion block is maybe running on another thread. I've also found that the problem can be triggered by a faulty test that ran earlier, so it isn't always caused by the test the failure manifests in.

Also, watch if you're spawning lots of async blocks (I do this for crazy thread safety tests, or tests checking exclusive access to a resource is working as expected, and in the order expected). Once you fulfil the expectation, the testing moves on to the next test, but if you've fired off a lot of async blocks after that point, they're maybe still running.