A Subtle Introduction To Mocking
What is Mocking ?
Mocking is simply the process of replacing an object with a fake that can act as a replacement.
Why Mock ?
The major reasons why we mock are Dependency elimination and removal of side effects. Think things like databases, 3rd party API requests and network requests, code that has to hit the filesystem.
This stuffs aren’t always guaranteed to be available or can prove tedious to set up (an internet connection for example) and even when they are (a logger that writes to the filesystem for example), they always tend to make your tests run extremely slow which is . The
F in Uncle Bob’s rule of unit testing (F.I.R.S.T) stands for Fast.
Unimplemented code can also be mocked. But i’d only recommend this if there is an interface the code is to adhere to.
Mocking boils down to the fact that a unit test is meant to be run in isolation so as to properly verify it’s functionality. When you aren’t testing in isolation, it is no longer a unit test but an integration/functional test, then mocking would be a terrible idea since the latter is meant to test the entire application as a whole.
Mocks versus Stubs
This terms are usually thrown around and it’s nice to make a distinction between both of them. In fact, both are actually categorized together. They are called Test Doubles.
Stubs are a set of fake/dummy data in other to make a test pass WHILE Mocks (object) are simply “fakes” that simulate the behaviour of another object in a way that is controlled by you, the tester. Mocks may (not) have stubs.
The defacto tool for this - in PHP - is Mockery even though PHPUnit ships with it’s own implentation. That can get too verbose.
To install mockery, we’d have to pull it in from packagist by running
If you want to make use of PHPunit’s default mock implementation, you can find an example here
How do i mock
In this section, i would be showing 2 practical use-cases of a mock. The sample code includes ;
- A Logger.
- An app that communicates with the Github API. (This would be in the second part of this post)
After hitting Github’s api, we want to log the username that was searched for. Probably to implement some sort of most searched users feature. The way this would work is by appending some sort of separator -
; - to each username in the log file. With this, we end up with a file that has it’s content similar to this :
Then we can run through the entire file content, run some logic to get the username that appears the highest number of times.
This obviously is a stupid idea but it passes the idea through.
In other to pass this idea through, I would be providing code samples that show our logger in two states : The premock stage and The postmock stage.
Both stages work as expected and have unit tests.
I have put up the code for both stages on github
The Premock Stage
So we have written and tested our logger. Awesome, we are green. But we have a problem, we touched the filesystem 5 times. What if we had to do this 15, 20, 50 times. Our tests’ would take longer to run. Initially, that may not sound as a bad idea but :
Tests should be fast. They should run quickly. When tests run slow, you won’t want to run them frequently. If you don’t run them frequently, you won’t find problems early enough to fix them easily. You won’t feel as free to clean up the code. Eventually the code will begin to rot — Uncle Bob (F.I.R.S.T)
Ok, that was a soft argument.
From an OO point of view, our
Logger class does too much thus violating the Single Responsibility Principle. How about we extract the
file_put_contents part to a class of it’s own - a class whose sole responsibility is interacting with the filesystem. Then our logger would just be a logger.
We also are not interested in testing the
file_put_contents (this goes back to side effects), it is a BIF and most likely have it’s own tests from
PHP’s core team (plus it is proven).
How about a rewrite ?
As said above, we can (and really should) split our
Logger into a
Logger and a FileSystem object. Let’s see what that looks like after the extraction.
This is pretty much straight forward. We have a dependency on a Filesystem object and on the
log method call, we append the username that was searched for to the file.
So how does this differ from the original implementation ? The major difference is that we are now injecting our dependency -
FileSystem - into our
Logger. This is to allow maximum testability as the built in
file_put_contents doesn’t have to be called (remember we are going to mock that. And also prevent the side effect it brings alongside it’s usage).
Well let’s write some test.
And we are green again without much ado - The
FileSystem class doesn’t need to be fiddled with again plus we have gotten rid of the side effects .
But hey, how am i sure the
FileSystem class is working ? Nice one. It should have it’s own tests.
Watch closely, this is probably the most important part of the tests. Don’t get carried away. Whatever you mock MUST have it’s own test(s).
phpunit again should still give us green and checking the
app.log shows we didn’t touch the filesystem.
app.logfile would have this values :
fabpot;codeguy;philsturgeon;funkatron;adelowo;. This is so as the premock tests touched the filesystem. You can clear that out and run only the tests of the post mock stage by running
Mocking is a big deal when it comes to testing. Easy to get started with. Easy to love. Easy and prone to misuse.
I hope this has given a little insight into how mocking works. I hope to write the second part soon.
Update : The second part is accessible here
Update => Dec 9
Just as it has been pointed out in the comments, the
put method should have it’s own tests - i kind of skipped this since i was only interested in mocking but some paragraphs up, i actually talked about mocks having their tests. There are actually three approaches to testing the
put method :
Touching the filesystem for real. If using this method, you would have to create a temporary directory in the
setUpmethod, then clean up/delete the directory in the
tearDownmethod. This has the disadvantage of making your tests run slow but some tips for speeding it up has been talked about extensively in the comments.
Virtually touching the filesystem (recommended). Your test for the
putmethod would still touch the filesystem but this time, it would be a mocked filesystem. Below is an example taken directly from
phpunit’s manual - which uses vfsstream - that shows the usage of a virtual filesystem instead of manually creating and deleting temporary directories.
- Overridding the
file_put_contentsfunction. This is actually possible due to the way
PHP’s namespace resolution works. Basically, you redefine the function - in our case,
file_get_contents- in a namespaced file.
Function or constant names that do not contain a backslash like name can be resolved in 2 different ways. First, the current namespace name is prepended to name. Finally, if the constant or function name does not exist in the current namespace, a global constant or function name is used if it exists. - PHP MANUAL and this - another manual entry
Below is an example ;
There you go, the
put method itself has also been tested and you can be confident it works. It is up to you to pick one of the methods you prefer. Personally, i’d go with the virtual filesystem option, but hey!!.
Thanks pantelis for pointing this out in the comment section.