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

Just, don’t.

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 expects() and 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.

The 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 array or 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 set() and 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 'bar'.

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 andReturnArg() and andReturnUsing().

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 andReturn().

Do you have another tip for using Mockery? You can check the Mockery Documentation for more tips, and check out how to install Mockery in your project in the official repository.

--

--

--

Graphic Designer graduate. Full Stack Web Developer. Retired Tech & Gaming Editor.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Notion API with Python

Introduction to Linux: Part 2 — Time To Draw Swords

Test and Merge new developer’s code in Master Branch for Production using Automation with DevOps…

Explain Interfaces in Golang with an example

Apache Zookeeper: Does Kafka need it?

Kubernetes vpn strongswan

LeetCode — Find All Numbers Disappeared in an Array

Laravel: reCAPTCHA v3 the easy way

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Italo Baeza Cabrera

Italo Baeza Cabrera

Graphic Designer graduate. Full Stack Web Developer. Retired Tech & Gaming Editor.

More from Medium

Migrating From PHP 7.4 to PHP 8.0

An image of migrating birds in a sunset sky

PHP: Use your private repository in Composer, without SSH keys

Php 8.1 New Features — Readonly Properties

Voila! Symfony and PHP 8.1