DEV Community

Peter Fox
Peter Fox

Posted on • Originally published at Medium on

PHP: Mocking Closures and performing assertions


Photo by rivage on Unsplash

Testing is crucial to ensuring code quality and reliability in modern PHP development. One powerful technique in testing is mocking, which allows you to simulate and control the behaviour of dependencies. Mocking closures can be useful when verifying interactions with anonymous functions or passing them around as first-class callables. This article will explore how to mock or spy closures in PHP using the popular testing library, Mockery.

What does our class that needs testing look like

For an example Class I will use a Laravel Validation rule. They’re simple classes that only implement one method, but the third argument for the method is for a Closure.

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class Uppercase implements ValidationRule
{
    /**
     * Run the validation rule.
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (strtoupper($value) !== $value) {
            $fail('The :attribute must be uppercase.');
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

How can we easily test this class? Let's make a closure that manipulates a boolean to set it as true if it has been called.

<?php

class UppercaseTest extend TestCase
{
    public function testItFailsNonUppercaseValues()
    {
       $calledClosure = false;

       (new Uppercase)->validate(
           '', 
           'lowercase',
           fn () => $calledClosure = true
       );

       $this->assertTrue($called);
    }
}
Enter fullscreen mode Exit fullscreen mode

That solution would be fine, but this might become difficult to manage for more complicated situations. For example, what if the Closure is called more than once? Or you want to check which parameters it’s received.

This is where it’s great to use Mockery.

Using Mockery and ShouldHaveBeenCalled

Solving this with mockery isn’t too obvious if you’ve looked through the documentation even. We can do it using the following code:

<?php

class UppercaseTest extend TestCase
{
    public function testItFailsNonUppercaseValues()
    {
       $closure = Mockery::mock(function () {
       });

       // we have to turn the mock into a Closure by using
       // a first-class callable
       (new Uppercase)->validate('', 'lowercase', $closure(...));

       $closure->shouldBeCalled()
          ->with('The :attribute must be uppercase.')
          ->once();
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see in the example, we can now nicely check if the Closure has been called with a string for our validation message and we can check that it was only called once.

Conclusion

Mocking closures in PHP using Mockery provides a flexible and powerful way to test your code. By understanding and leveraging this technique, you can write more reliable tests and ensure your code behaves as expected in different scenarios. The provided code example demonstrates the basic steps to mock and assert closures, which you can adapt and expand upon in your projects.

Remember, testing is not just about verifying correctness; it’s also about building confidence in your code. So, start incorporating mocking into your test suite and enjoy the benefits of more robust and maintainable PHP applications.

If you’re struggling with using Mockery feel free to reach out as it’s personally a topic I enjoy talking about. Happy coding!

I’m Peter Fox, a software developer in the UK who works with Laravel. Thank you for reading my article, I’ve got many more to read at https://articles.peterfox.me. I’m also now Sponsorable on GitHub. If you’d like to encourage me to write more articles like this please do consider dropping a small one-off donation.

Top comments (0)