Unit testing does not always rhyme with simplicity but isolating your unit of code will become one of the most useful skills in your testing journey. Find out more about how mocking works and the terminology around it.
Mocking is defined as the set of different types of techniques which help achieve the goal of isolating units or pieces of code in order to test them in an independent way while simulating the environment in which the unit of code exists. Mocking techniques are called test doubles and the two most common ones are Stubs and Spies (other test doubles such as dummies and fakes exist.)
Let’s take this function for example:
If you want to test its logic, you merely have to call the function with a non-empty string.
No mocking is needed here. You can write a test as simple as this:
Let’s now look at another function:
If you want to test the logic within this function, you have to control the return value of Math.random (otherwise, your test would be flaky, which means it would pass sometimes and fail at other times). This is called stubbing.
Here is an example of what a test for this function could look like:
We have updated Math.random so it always returns 1, which to be fair is still pretty random, as illustrated in the famous meme:
Note that the original implementation of Math.random was never called, because it was stubbed (= replaced) instead.
Several frameworks allow you to stub easily, by taking care of implementation details for you. We’ll offer a comparison in a future article.
Here is another interesting aspect of mocking
If you remember the AAA pattern, then you know that the point of unit testing is to write assertions. But what should you assert on? By default, you can only observe what happens outside the function:
However, you might also want to test some logic that happens inside the function itself.
For example, you might want to make sure the database has been queried in the following function. This is called spying:
In order to achieve this, you could write a unit test that looks like this:
Observe that we respected the AAA pattern: steps 1 and 2 are Arrange, step 3 is Act, step 4 is Assert.
The fake function that we wrote at line 2 is called a spy.
Note that in practice, unlike stubs, spies don’t prevent the original function from being called. In our example above, the database will be queried in real life. If you don’t want that to happen, you might want to use both a spy and a stub.
Also note that mocks (stubs and spies) should be restored. This means resetting to the original implementation of the function. You don’t want your stubs and spies to influence other test cases that did not expect some functions to be mocked.
Here is an illustration of how restoring works with Math.random:
Fortunately, all mocking frameworks expose nicer ways to mock and to restore functions. For example, with Jest you can just write
In a typical unit test, your modules should be isolated. There should be no interactions between all the parts of your code.
You want to avoid a unit test to make API calls, rely on a database or write to your file system whenever it is run – whether from your command line or a CI. The use of mocking is the magic tool that allows your modules to be isolated.
On the contrary, you are unlikely to find mocking in integration tests.
Mocking also allows you to make assertions on side-effects of your function that could not have been tested otherwise (i.e.: how would you otherwise make sure the database was queried, if there is no database?)
Mocking is a precious tool, but it’s not universally loved.
Some common reasons for disliking mocks, are that
In particular, there are some specific scenarios where mocking is not recommended, here are a few:
The reason for this is obvious: integration tests should validate the correct interaction between different modules and parts of code as they would happen at the time of running the application. For this reason, isolation through mocking defeats the point of integration tests themselves.
With mocking, your unit test becomes aware of the internal implementation of your function: it knows what is going to be called. This is called whitebox testing.
Say you change the implementation of addUser, and your function now calls mysql2.query instead of db.query. The unit test verifying this call will be broken.
Of course it will be easy to change, but without mocking there would have been less maintenance. Writing unit tests that are unaware of the internals of the tested functions is called blackbox testing.
Sometimes you just can’t avoid stubbing in order to run your test , but in particular in the case of spies, you should always ask yourself what makes sense to assert.
If you want to make an assertion on an object in which some properties are non-deterministic, it is worth considering adapting the test rather than mocking indiscriminately.
Take, for example, the scenario below.
Two options are available to you:
You could write a test of this kind, but it would be a little verbose:
Alternatively, you can simply use one of the following two alternatives, which achieve the same result of giving confidence in your code without the need for a stub.
With Ponicode Unit Test extension, you can create mocks in one click, without learning any framework. Right click on the function you want to mock, and Tadaaa! the mocking code will be generated for you. Learn how it works with our illustrated documentation.
We have presented two common uses of mocking. It’s time for you to get started but remember: great power means great responsibilities - mocking is a great testing technique as long as it remains simple.
Do let us know what you think about it, and what your favorite tools, strategies, or libraries for mocking are. Or the worst mocking code you’ve ever seen!
Subscribe to our newsletter for more tips on mocking!