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

Jack Bayliss contributed the change in #61770.