swiftxcodeasynchronousxctest

Can you run an XCTest only after another has completed (and if so, can it be conditional on the prior test passing)?


For example, I don't want want to run test of the functions that select from my local data, until the test that populates the local data with the icloud data in has succeeded.

I think a proper unit test should write some predefined local itself, data before testing the local data selection methods (do people agree?). However, the calls to iCloud are asynchronous and could therefore return midway through this other test, so it would still need to be delayed until they have completed.


Solution

  • Unit tests are supposed to be atomic and independent, so generally this approach is not recommended. XCTest does not support this by default either. There is a discussion in this Stackoverflow post about overriding +testInvocations in Objective-C to return NSInvocation objects in the order that you'd like.

    There are a couple options to consider here. One is to perform the initial test configuration in setUp. Global setup that will be performed once before all test cases run can go into a class level setUp method. If you would like to perform the setup before each test is run, then you create an instance level setUp method. (There are also matching tearDown methods that execute after the tests have run). The XCTest documentation has more details here. Note that you'll need to use the semaphore pattern described in this Stackoverflow post to ensure that any async calls complete before running the tests.

    I think the most straightforward way to handle this is to include the iCloud calls in the same unit test using the expectationWithDescription class. In short, you will perform these four steps:

    1. Instantiate a new expectation, which signals when the async logic has been completed.
    2. Perform your asynchronous operation (i.e. populating data from iCloud). In this operation's callback, you can include more logic as well as assertions.
    3. At the end of the callback's method (generally), you can fulfill() the expectation. That will signal that the expectation has been met.
    4. After your async block, call the waitForExpectationsWithTimeout() method. You will invoke this method with a timeout period; if the expectation has not been fulfilled in this period, you can fail the test, log output, etc.

    An example of expectation code is also provided in the Stackoverflow post that covers async calls.