Injecting mocks in Laravel tests

If you’re doing much testing in Laravel, you may have found yourself writing a lot of code like this for mock objects:

$mockService = Mockery::mock(PaymentService::class);
$mockService->shouldReceive('voidPayment')->andThrow(new PaymentException("Couldn't void payment"));
app()->instance(PaymentService::class, $mockService);

Sometimes you’ll find yourself duplicating versions of this code throughout a class if you’re testing a lot of interactions with an outside service. It’s not a big deal, but it’s a little tedious. (Also, I tend to forget the last line and not understand why we’re hitting the real service instead of the mock.)

I’ve made myself a little shortcut:

// TestCase.php
protected function bindMock(string $className, callable $configuration)
{
    $mock = Mockery::mock($className);
    call_user_func($configuration, $mock);
    app()->instance($className, $mock);
}

Once that code is in my base TestCase class, I can use the following code in my test cases:

$this->bindMock(PaymentService::class, function ($mock) {
    $mock->shouldReceive('voidPayment')->andThrow(new PaymentException("Couldn't void payment"));
});

It’s not a huge simplification, but I do think it’s a little easier to read and easier to not screw up.

Leave a Reply

Your email address will not be published. Required fields are marked *