Swift : Asynchronous Code Unit tests, Xcode project — Part 2

Varun Tomar
4 min readMay 5, 2020

--

Continuing our discussion from Part 1. We have learned how to write some basic Unit test for synchronous code, but we can’t limit ourself to synchronous code testing. In general, we have lot of asynchronous code in our project like network calls, delayed operations, background tasks and more.

Why unit testing async code is a problem?

What unit test do ?? “It verifies a known and fixed input resulting a known and fixed output”. This means we know about input and expected output but in case of async code, we can’t verify it as we don’t know when async code gets finished. Let’s discuss some way to cope up with this problem.

  1. XCTestExpectation

Let’s say we have a service class, in which we make network calls and passing a callback handler to get the result. Please refer below image :

Sarvice Manager Class
ServiceManager usage

If we write a normal unit test for above function then we may get deceiving output, this happens because by default XCTest is synchronous, but what we are trying to test is asynchronous. It means that the execution of the tests goes to the next line of code right after the async method is called, and the whole test finishes before the callback closure is run.

Now here comes XCTestExpectation in the picture, let’s write unit test case to test async code. Please refer below image.

Async code unit test

Here are three steps as shown in above image :

  1. Add an expectation with a meaningful description. It is necessary because this is reported in the failure message of an unfulfilled expectation.
  2. Next execute the asynchronous function that needs to be tested. Fulfil the expectation at the end of the callback closure.
  3. This is where the test runner will pause, so that the asynchronous operations can be completed and assertions can be verified.

2. Inverted Expectation

Expectation not only used for waiting for something to happen but also for reverse possible. Means inverted expectation can also used to verfiy that something didn’t happen. Let’s frame an example, referring to above case if we have to cancel that async call based on requirement, replacing that call for some searching task. If user types in search bar, we don’t want to perform network operations on every edit in search bar. To test this our ServiceManager class actually cancels any pending closure when a new one is scheduled, here we can use an inverted expectation. Please refer below image :

3. Using Dispatch Queues

As we see “expectations” are very useful in various situations but we can also make use of dispatch queues for asynchronous code testing. Let’s frame an example; Suppose we have a task to compress an image, compressing image is a heavy task and should be performed in background using dispatch queue. Now the problem statement is how to test this? Below is a ImageManager class we are using to compress image.

Image manger class having function to compress image

In ImageManager class we have a function “compress” to process image, it’s running on background queue. To test it let’s create unit test class as shown below. The below test won’t pass, as it finishes before the completion is called.

To make that test work, first we need to inject dispatch queue via ImageManager init(). Refer below image

Now with this injection, we can mimic synchronous behaviour as follows :

queue.sync changed asynchronous behaviour of code to synchronous, with this trick our test case will be successfully tested.

TIP : queue.sync in above code is a trick to test asynchronous code by mocking it as synchronous.

This is not yet done as there is lot more stuff on Unit Testing, I will come up with more articles soon. Please clap if you find it useful and share your valuable feedback in comment section. Stay healthy, stay happy : 😃 😃

--

--

Varun Tomar
Varun Tomar

No responses yet