Mock functions in your unit tests
Complete unit test coverage says nothing about your code. But once you're actually trying to cover all your code paths, it can be quite frustrating that you can't cover everything, because you depend on the result of a built-in PHP function. In this blogpost, I will describe a way (call it a hack or dirty code, I don't care) to mock those built-in PHP functions so you can achieve complete coverage. This one is for the 100%-junkies!
A quick example
Here's a small example of what I encountered while working on a side project:
As you can see, there is a certain branch of the possible code paths which relies on the failure of receiving a resource from the
Since I wasn't able to control what's returned from the
proc_open function, that part of the code was never covered by a unit test.
Thanks to a, let's call it creative, usage of namespaces, controlling the result from
proc_open now is possible.
In short, the method can be described as "Re-implement the desired function in a separate namespace and then call the original method when necessary".
The important parts here are:
- Use the same function definition
- Create it in the same namespace as the System Under Test
- In the SUT, don't use the function from the root namespace (
\proc_open(...)), or this method will fail.
This allows you to create a unit test which tests that if-branch.
Wait, there's more!
This is all fine and dandy, but the hard-coded
return false; now causes all functionality (and tests) to break. We need to be able to control when to
To achieve this, we need to play around with namespaces even more:
Since we cannot alter the function signature, we can't add a variable to indicate that we want to receive a
false or a real result. We need to rely on a global variable. (Yikes!) We define that global variable in the root namespace, and initialize it to false. Then in our stub-function, we import that variable through the
global keyword. If we set that variable to
false, we return an actual result from the original function. When that variable is set to
true, we return our other result.
Now to use this in a unit test:
Import the global variable
$mockProcOpen in your unit test method (here I used the
setUp functionality), and then set it to the desired value.
This now enables you to cover that if-branch, and achieve 100% coverage.
Is it possible to now mock/stub built-in PHP functions? Yes. Is it dirty? Yes. But hey, no-one ever said you cannot write dirty code once in a while to achieve 100% coverage, right? ;-)
In all seriousness, only when that 100% coverage is really important to you, you should resort to such measures. Thanks to namespaces, it now is possible to do this. Whether you actually want to use this, is up to you.