PHP: 10 tips to use for Mockery
Because testing doesn’t have to be a 1,000 line per-test odyssey
For those who don’t know what mocking is, its the technique to create a “fake” object from another, and assert functionality, like the methods called. It’s super useful when you want an object to offer expected functionality, but without working as the real one, like something that sends HTTP Requests or uses files.
PHPUnit already includes what’s called “Test Doubles”, but it’s somewhat basic as it’s not the focus of the test framework itself. Nonetheless, is still a good tool to have, but not something I use a lot.
Instead, I mostly use Mockery, a library to mock objects for testing. It’s not a shadowy library, instead it past 160 millions downloads at time of writing. The syntax is very straightforward and short enough to understand what you’re doing, plus is very flexible when expecting or asserting things.
After working so many times with it, this are my top 10 tips I wish I could knew before i put the first line of code.
1. Don’t use static
Long answer? Well, it’s okay to use protected or private static properties that don’t affect the state of something, as a way of helping a dynamic method, but that’s it, these are untestable and difficult or plain impossible to mock properly. Think of static methods as functions with a bonus namespace.
For the case of static properties, also don’t. You may use the Service Container pattern to hold a shared instance of an object, that would reset back to the original state every time a new test is executed.
There are numerous problems with static properties, and testability is one of them. So, unless you expect to treat them almost like constants, values that should only be set once in the whole application lifecycle, or exclusively inside a test, save yourself a problem.
If you’re out of options, then remember to always reset the original state of an object whatever you can.
2. Shorter your syntax
Some years ago, Mockery implemented the
allows() methods that can take out some not-useful lines from your testing code.
Compared to the legacy syntax, the new modern syntax can save you some keystrokes and make it more understandable.
3. Use partial mocks when possible
When you want to test if an object receives something, you may think about implementing all the methods that should be called to get to the one you need to test. Well, you don’t need to set up the whole expectation chain.
Mockery allows to mock the class partially. In other words, the object will work as normal except for those methods you set to be expected.
This can save you a lot of lines that deal with useless expectations just for the sake of having the object usable. Indeed, sometimes my projects have more partials than completely mocked objects.
The above also makes the class never execute the original constructor, which may be desirable when these contain a lot of logic that can get in the way of testing. For example, when a class, during instantiation, decides to also throw an Exception because something wasn’t set up for real.
4. Checking on arguments
When you want to do some checks on what a method receives, you can use
withArgs(). It accepts a function that will receive all the arguments passed down to the expected method. If the function doesn’t return truthy, it will not pass the expectation.
This is an useful place to also make assertions and just return
true, as the test will fail anyway if one of these assertions don’t pass. This way you don’t need to explicitly take out the value into test context, or use other complex statements to check if an argument is what you expect.
On the other side of the fence,
withAnyArgs() just checks if it receives at least one argument, whatever it may be.
5. Argument types
Sometimes you just want if the method receives an argument type, regardless of the contents. This is handy when a method can receive anything, like an unpredictable or random value, or when it receives an union type (more than two types) and you only want to check which one.
Mockery::type() method helps with that. When the mocked method receives the argument, it will check if the type of this argument is the same as the type you expect. It accepts primitives like
int, to class names.
6. Chaining methods in one string
One of the problems I had with mocking an object was method chaining. An object was called with multiple methods like this:
Any respectable developer would mock the object for all methods, and say to always return itself. This would only grow as the same as the number of methods, so this can get pretty messy when you have dozens.
Instead, if you’re willing to disregard the arguments of the methods, but still ensure these are called in the right order, you can chain all of them in one sitting using
7. Ordering methods
On some objects, calling a method after the other yield different results than in reverse. For example, multiplying after a subtraction is different than subtracting and then multiplying.
For these cases, the
ordered() method will make your expectations execute in the order these are set.
8. Public properties can be (sometimes) your friends
Let’s make an scenario. An object method called “process” receives a value, transforms it, and then it puts the result as a public property called “sum”.
We can mock the
process() method, but we can’t mock the public property. Worse, we depend depends on this property value to continue working later. Kind of bad pattern, but sometimes you may find yourself in these rare cases. While I would decouple that functionality into methods because these can be tested, you’re out of luck if is a dependency you cannot edit.
In these cases, may be
andSet() methods will save your day.
These both can be chained to an expectation, and will properly set a public property value that other objects will be able to access to. The second allows to change what is set based on the number of executions.
I totally expect this will be more used as
readonly properties start to proliferate on modern code as these are already supported on PHP 8.1.
9. Spies are your friends
Instead of just making a full mock, or a partial mock, you can use a spy. A spy is a way to assert if some methods were or not called after the object was used.
The object being spied will execute normally, but Mockery will count all the methods that were called, so you can assert on them after the fact.
This is mostly useful when the object you want to spy does nothing harmful if it’s live.
10. Mockering magic methods
Mockery is not friend of dynamic methods calls. For example, calling a method that doesn’t exists in a class won’t be mocked because is considered not part of the class itself… which is totally correct. That’s no problem, because you’re still able to expect the magic method.
Setting expectations for a magic method will expect it anyway.
For example, when this object receives
setFoo('bar'), which is a method that doesn’t exists, the
foo property is set as
Bonus: Returning the same arguments
When expected methods are called, you also have the opportunity to set what it returns. Luckly, Mockery allows to further modify the what it returns based on the arguments it receive using
The first returns the argument verbatim based on the index of the argument, while the latter accepts a callback to further change the end result.
You’re free to do whatever you need to return something consistent. You can also queue them by adding multiple callbacks, which is the same as with