laravelmastery
SnippetsArticlesSeriesNews

Test-driven Laravel · Lesson 3 of 3

Test-driven Laravel · Lesson 3

Faking the queue

Edges and judgementTesting

Issuing the invoice is the feature this series tests. Marking the order paid is the payment step after that, and the job is the one from the job class lesson. The happy path dispatches a job. The test should prove the job was pushed, and it should not run the job. Queue::fake() records dispatches and runs nothing, so a payment job cannot charge anything during the test.

Video

01 Assert the class and the order it carries

Fake first. Then make the request. Then assert the job class and the id it was given.

PHP
it('dispatches a job to mark the order paid', function () {
    Queue::fake();

    $user = User::factory()->create();

    $this->actingAs($user)
        ->post('/orders', [
            'number' => '1042',
        ])
        ->assertRedirect();

    Queue::assertPushed(MarkOrderPaid::class, function (MarkOrderPaid $job) {
        return $job->orderId === Order::query()->firstOrFail()->id;
    });
});

The order row is still there to assert. The job’s handle() did not run, so the status is still pending. If the test needs the status to become paid, call the job’s handle() yourself, or use Queue::fake() only in the test that cares about dispatch.

Bus::fake() is the matching tool when the code dispatches a batch rather than a single job. assertPushed will not see a batch.

02 Run handle() when the test is about the status

Queue::fake() is the wrong tool for “the order becomes paid”. That sentence is about handle(), not about dispatch. Call the job with the id the request stored. The status assertion then belongs to the job, and the dispatch assertion stays in the test above.

PHP
it('marks the order paid when the job runs', function () {
    $order = Order::factory()->create(['status' => 'pending']);

    (new MarkOrderPaid($order->id, 'attempt-1'))->handle();

    expect($order->fresh()->status)->toBe('paid');
});

Do not combine them. A test that fakes the queue and then calls handle() is two acts. The fake test proves the controller dispatched. This one proves the job writes paid. A retry of handle() is Make the job safe to run twice, and it expects the second call to leave the row alone.

assertPushedOn('payments', MarkOrderPaid::class) belongs in the test only when the queue name is part of the behaviour. The job class lesson sets that name in the constructor. Asserting it here catches a dispatch that forgot the class and landed on default, where a report worker would delay the payment.

Recap

You can prove a job was pushed without running it, and call handle() only in the test that is about the status.

  • Queue::fake() runs nothing, so a payment job cannot charge during the dispatch test.
  • assertPushed can read the id on the job.
  • A batch is Bus::fake(), not assertPushed.

Check yourself · 3 questions

Question 1 of 3The test fakes the queue and asserts the order is paid. Why is that the wrong pair?

The job was pushed, and handle() stayed unrun. Next, keep the PDF off the real disk.

← PreviousFaking the mail

On this page

  • Assert the class and the order it carries
  • Run handle() when the test is about the status
  • Recap
  • Check yourself

Test-driven Laravel

  • 1 Overview

The shape of a test

  • 2 What a feature test is for
  • 3 Arrange, act, assert
  • 4 Refreshing the database
  • 5 Factories that read like the domain
  • 6 Acting as a user

The paths that matter

  • 7 HTTP tests for happy paths
  • 8 Validation errors
  • 9 Authorisation

Edges and judgement

  • 10 Faking the mail
  • 11 Faking the queue
  • 12 Faking storage and HTTP
  • 13 Freezing time
  • 14 Datasets
  • 15 What not to test

Test-driven Laravel

Lessons

  1. 01Overview

The shape of a test

  1. 02What a feature test is for
  2. 03Arrange, act, assert
  3. 04Refreshing the database
  4. 05Factories that read like the domain
  5. 06Acting as a user

The paths that matter

  1. 07HTTP tests for happy paths
  2. 08Validation errors
  3. 09Authorisation

Edges and judgement

  1. 10Faking the mail
  2. 11Faking the queue
  3. 12Faking storage and HTTP
  4. 13Freezing time
  5. 14Datasets
  6. 15What not to test

Get the Friday email

One snippet and the week’s Laravel news, in your inbox every Friday morning. Unsubscribe whenever you like.

laravelmastery

Practical Laravel learning for working developers, made in the UK.

Learn

  • Snippets
  • Articles
  • Series

Stay current

  • News
  • Friday email
  • RSS feed

Company

  • About
  • Privacy
  • Terms
© 2026 Laravel Mastery. Independent and not affiliated with Laravel Holdings Inc.Designed and built by Half Shell Studios

We remember the videos you watch, where you left off, and which series lessons you mark as read, against this connection’s address. Nothing is stored until you agree.