How to test Combine code in Swift
Learn how you can test your Combine code by using expectations
Testing our code is a fundamental part of the development process.
But, if we're using the Combine framework, this could be unintuitive at first sight. That doesn't mean that it can't be done. We just need to dig into some more advanced concepts that Swift provides us.
Let's take a look at how we can easily include unit tests in our Combine code.
Let’s start by looking at the following manager that simulates handling a traffic light.
From our initial Combine article we know that by adding @Published wrapper, we get a free Publisher that we can access by using the $ character.
Testing
Before we dig into how to actually test Combine code, let’s review some core concepts regarding unit testing. We can summarize it in three steps.
- Step 1: Define what you want to test.
- Step 2: Execute the operation you want to test with the value you defined in Step 1.
- Step 3: Evaluate the result that you got from Step 2.
Take a look at the following example:
This is a very simple example so you can understand the concept. We first defined our expected value for the test, a red light. Then, we call an operation that changes the current light value to red. And finally, we make sure that the current light’s value is the same as the expected one.
This is fine, but what should we do if we want to test simulateFlow
function? You can notice that inside that function we are changing the light property three times. So, at the end of the function's execution, the property's value should change from green to yellow, from yellow to red, and finally from red to green.
As you can imagine, in order to test the function, we need to make sure that the property’s value, has the right value at the end, but most importantly we must be sure that all the value changes (from one to another) are correct.
Expectations
XCTestExpectations are a type of object that Apple provides to us for testing asynchronous code. Basically, you create an expectation at the beginning of your test and when certain conditions are met you fulfill that expectation. The test’s execution will wait until the expectation is fulfilled or when a timeout is expired.
So, in order to test our function, we will need to wait until we collect all the expected values that we know currentLight
property should change. Then, we will fulfill the expectation if we got all the values. Otherwise, the test will fail because of the timeout.
With this in mind, we now have the right tool to create a new test for our simulateFlow
function
Let’s breakdown the code:
Step 1
- We create a new expectation to use in our test.
- We defined the expected values that we know the property should change at some point.
- We also created a new empty list that we are going to use to store the values that we’ll receive from the Publisher.
Step 2
- We start listening to the Publisher’s values.
- We call the function so we start receiving the property’s value changes.
Step 3
- We wait for the expectation to be fulfilled or for the timeout to be reached.
- We evaluate if the received values that we got are correct.
Conclusion
Testing Combine code is pretty easy once you understand the potential of expectations. Although we saw a very short and simple example, you can use this same technique for testing more complex solutions.
For instance, you can apply these principles to test your ViewModel’s states in an MVVM architecture, or to test some mocking network calls.
You might want to check my previous articles regarding Combine.