Welcome to the fourth part of the Unit Testing in Swift series!
In this article we will look into implementing custom mock classes to be in complete control of your code while unit testing. If you are new to unit testing and want to learn how to start unit testing with Swift in Xcode, I suggest you take a step back to Part 1: The Fundamentals of the series, before reading further. And if you are wondering why mocking is even necessary, you should definitely have a look at Part 3: Proper Architecture.
This part of the series is for you, if you want to learn how to implement a basic mocking system that lets you take control of all potential scenarios in your unit tests. The examples in this article, use the classes
C as defined in Part 3: Proper Architecture and I highly recommend taking a step back if you haven’t read part 3 already.
Where to begin?
In the previous part we ensured that our
C classes were using dependency injection and the facade design pattern allowing us to mock the dependencies. So let’s continue where we left off: Mocking the
To recall, here are our two protocols that we need to implement:
Creating the mock
Creating a mock class is as simple as conforming to the protocols. For the original concrete implementations of
C we wanted to make sure to avoid public mutability, but the opposite is the case for mock classes: Public mutability is a must in order to unlock all testing scenarios. Concrete mock classes conforming to
C may look as such:
C does not require any specific code, as the protocol only exposes a property that we can make mutable. Class
B, however, is different because it exposes a function. As we want to be able to control what the function is returning, we’ve created a mutable variable
validateAnswerResponse that we return from the function, once again allowing us to be in complete control of the test scenario.
Note: Remember that these classes should only be included in the unit test target of your project — we do not want people to accidentally instantiate mock classes in your production target.
Using the mock classes
With our two dependencies mocked, we are ready to improve our unit tests with as many scenarios as we like:
Notice how we use our
BMock class in order to test all possible outcomes of the
checkAnswer() function. In this case there are only two scenarios, but when testing the
validateAnswer() function of
ConcreteB see how the mock class allows us to test every single scenario of the function. In the example below we are creating 100 different scenarios to test.
It becomes clear that our mock unlocks a number of scenarios equal to the number of distinct integers, hence testing all potential scenarios is clearly not feasible. However my personal preference is to test all high risk scenarios and a few extreme cases (if applicable) — Having too many tests is always better than lacking potential scenarios.
Congratulations, you are now in complete control of your unit tests — it feels good, right?
This is just the beginning…
Mocking can be as simple as shown in the above examples, however with mock classes you have the opportunity to improve the quality and reliability of your unit tests even more. In the next chapter, we’ll look into how we can enhance our mock classes and take your unit tests to the next level, with functionality for asserting the behaviour of your functions rather than only focusing on the logical outcome.
As always, if you have any questions or comments, feel free to reach out to me by commenting on these articles. I will reply to all messages.