Checking which job was pushed, or which event or notification was sent, usually means writing a callback that compares each property. Starting in Laravel 13.35, the Queue, Bus, Event, and Notification fakes accept an array of expected property values in place of that callback.
Before and After
Take a test for a podcast upload that pushes a ProcessPodcast job. Before 13.35, checking the job's properties took a callback:
Queue::fake(); $this->actingAs($user)->post('/podcasts', $data); Queue::assertPushed(ProcessPodcast::class, function (ProcessPodcast $job) use ($podcast, $user) { return $job->podcast->is($podcast) && $job->user->is($user) && $job->status === 'pending';});
With 13.35, the expected values go in an array:
Queue::assertPushed(ProcessPodcast::class, [ 'podcast' => $podcast, 'user' => $user, 'status' => 'pending',]);
The other fakes work the same way:
Event::assertDispatched(OrderShipped::class, ['order' => $order]); Bus::assertDispatched(GenerateInvoice::class, ['order' => $order]); Notification::assertSentTo($user, InvoicePaid::class, ['invoice' => $invoice]);
The array also works with the negative and variant assertions that take a callback, such as assertNotPushed(), assertPushedOn(), assertNotDispatched(), and assertNotSentTo():
Queue::assertNotPushed(ProcessPodcast::class, ['user' => $otherUser]);
Pass the class name as the first argument. The array cannot be used with the form of assertPushed() that takes only a typed closure.
How Values Are Compared
Every key in the array has to match for the assertion to pass. Each value is compared with strict equality (===), with one exception: when the expected and actual values are both Eloquent models, they are compared with $model->is(), which checks the primary key, table, and connection.
Strict equality has consequences for other objects. Two Carbon instances for the same moment are different objects, so 'scheduled_at' => now() fails. Use a callback when you need to compare dates, value objects, or collections:
Queue::assertPushed(SendReminder::class, function (SendReminder $job) use ($user) { return $job->user->is($user) && $job->sendAt->equalTo(now()->addDay());});
The array checks top-level properties only, so it has no dot notation for nested values.
Public Properties Only
The fake reads each property from outside the object, so the properties have to be public. A job that keeps its data in protected properties, or behind getter methods, still needs a callback. Constructor property promotion with public works:
class ProcessPodcast implements ShouldQueue{ use Queueable; public function __construct( public Podcast $podcast, public User $user, public string $status = 'pending', ) {}}
A key that does not exist on the object fails the assertion instead of throwing an error, so a typo in a property name shows up as a failed test.
Further Reading
- Route::query() and Model defaults() in Laravel 13.35
- Watch Jason McCreary's "Testing With Fakes" talk from Laracon
- Learn how to start Testing in Laravel with Simple Examples using PHPUnit and PEST
Jack Bayliss contributed the change in #61770.