logo

When Mockery says your Eloquent model doesn't match

Let Eloquent's is() method do the comparison

Aaron Saray
Aaron Saray
2026-08-13

In tests, it's common to have a service that takes an Eloquent model as an argument, and therefore you want to mock that service to confirm it was called with the right model instance.

The naive approach is to pass the model straight to Mockery with ->with():

$order = Order::factory()->create();

$invoiceMock = $this->mock(InvoiceGenerator::class);
$invoiceMock->shouldReceive('generate')
    ->once()
    ->with($order)
    ->andReturn($fakeInvoice);

This tends to fail in most cases. Mockery's ->with() does a strict equality comparison on objects, which means it only matches if the system under test receives the exact same PHP instance you passed in. If your service or path hydrates or retrieves the model, you will have a different instance representing the same database row. The test fails even though the behavior is correct.

In another tip, I showed how a validation assertion that's too loose can pass for the wrong reason. This is the opposite problem, a matcher so strict it fails for the wrong reason.

It's tempting to just not test this or to reach for testing specific values on the instance. But there's a cleaner, more elegant, and Eloquent way to do it:

$order = Order::factory()->create();

$invoiceMock = $this->mock(InvoiceGenerator::class);
$invoiceMock->shouldReceive('generate')
    ->once()
    ->with(Mockery::on(fn ($arg) => $order->is($arg)))
    ->andReturn($fakeInvoice);

Mockery::on accepts a closure and matches the argument when the closure returns true. The closure delegates the actual comparison to Eloquent's is() method, which uses Eloquent's internal matching system. Different instances still succeed, as long as they reference the same underlying row or model.

This isn't always the solution, but for 95% of the cases, I would say checking with is() gives you the proper coverage you need compared to checking individual attributes.

Here to help,

Aaron

P.S. Joel and I answer testing questions like this every day in our community. Come ask us yours.

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼
email
No spam. Only real-world advice.