Structural Mocking In Swift
I will demonstrate how to mock with a simple structure in this article.
Introduction To Mocking
As you may be familiar with other Mocking Framework, they are built on top of a Reflection feature, Allowing the framework to manipulate and modify classes in runtime and replace some implementation with their mocks, stubs, or spies.
But reflection is a threat for two reasons:
- Defeats Static typing
- Security
Reflection defeats notably static typing and can lead to run-time errors. This is what Swift was designed to be safe from the beginning. Swift is a compile-time or static typing language, meaning that all the safety checks should be performed on compilation so that the errors won’t arise on runtime and causes a crash. Reflection violates the safety of Swift.
Note that swift has reflection, but it is readOnly, which means there is no way to modify classes.
Except for those classes that inherit NSObject. That wouldn’t be swift, though:)))
Reflection is a security threat for apps that demand maximum data quarantine. A reverse engineer can easily decompile the code and add an extension that modifies the source code.
Does the question arise of how to mock objects in Swift?
How To Address Mocking In Swift
A bunch of libraries can be used to generate mocks or streamline the mocking procedure. For instance, the one I am obsessed with is Sourcery.
But why should we use a complex library to mock the objects?
Are they reliable and can test anything?
What if some changes occurred and failed all written tests so far?
Does it provide a significant advantage?
Many advocates of unit testing, including Uncle Bob, often say that mocking frameworks are overused.
So what advice does Uncle Bob give about not using mocking frameworks — something Swift developers will need to get used to:
I don’t often use mocking tools. I find that if I restrict my mocking to architectural boundaries, I rarely need them.
Mocking tools are potent, and there are times when they can be pretty helpful. For example, they can override sealed or final interfaces. However, that power is seldom necessary; and comes at a high cost.
Mocking across architectural boundaries is easy. Writing those mocks is trivial.
Writing your mocks forces you to give those mocks names and put them in particular directories. You’ll find this helpful because you will likely reuse them from test to test.
Writing your mocks means you have to design your mocking structure. And that’s never a bad idea.
In Swift, there is no other choice than to follow the advice from Uncle Bob: write your mocks. It’s a proven and sustainable approach — still, it would have been nice to have a choice.
Composing A Structure For Mocking
The mock testing process has four main steps:
- Given
- Expect
- When
- Then
The structure is going to handle steps 2 and 4. Here is what should be created:
- A protocol that mock objects can conform to(Mock).
- A class that manages the expectations and factual results(MockActions).
Starting by Mock:
There should be an Action type for setting expectations that can be inside the mocks. We can put as many actions as for the mock object. It is best practice to use Enum for action, which will be covered shortly on examples.
The two methods register and verify take the responsibility of registering action inside mocks and verify it from outside.
Let’s see what MockAction does:
MockAction is responsible for holding expectations and factual and also verifying whether the expectations are fulfilled or not.
Before comparing expectations and factual, we also check if any factual are ignored or not by filtering them with the help of shouldIgnoreAction.
Example
Consider the service below that converts any number of different bases to Decimal.
First, the mock conforms to Mock protocol and defines its Action.
Then the method which should be tested can register some actions that can be verified.
Time For Testing
As mentioned before, the four steps for a unit test are Compliance.
Congrats, you have learned to use such a simple structure for mocking and release yourself from using a framework or library. Uncle bob appreciates that:)))
The project Mockia is in its first steps 🥹 and I will try to develop it continuously. Also, your ideas and pull request are welcomed.
Have a lovely Weekend, fellas.😎😎😎