laravelmastery
SnippetsArticlesSeriesNews

Test-driven Laravel · Lesson 1 of 3

Test-driven Laravel · Lesson 1

Refreshing the database

The shape of a testTesting

Arrange, act, assert only works if the previous test left no rows behind. Each test needs an empty database. A row left behind by the previous test makes the next one fail for a reason that has nothing to do with the invoice. RefreshDatabase migrates the testing database once, then rolls each test back.

Video

01 Point the tests at their own database

Use the trait from the Pest file, or on the test class. The connection is the one in phpunit.xml, not the database the site is serving.

PHP
uses(RefreshDatabase::class);

it('starts from an empty invoices table', function () {
    expect(Invoice::query()->count())->toBe(0);

    Invoice::factory()->create();

    expect(Invoice::query()->count())->toBe(1);
});

The invoice from this test is gone when the next test starts. That is the transaction rolling back, so do not expect a job process outside the test to see the row: it was never committed. A test that must commit uses a different path. This series does not need one.

Never aim RefreshDatabase at the database you use in the browser. Migrate and roll back belong to the testing database only.

02 The testing connection is a different database

phpunit.xml, or the Pest environment, points DB_DATABASE at a database that exists only for the suite. The site’s database keeps its rows. A test that creates an invoice writes to the testing database, and the transaction rolls that write back before the next test.

PHP
<env name="DB_DATABASE" value="laravel_testing"/>

If a test seems to see a row it did not create, it is either missing the trait or it is connected to the database you browse. Check the connection inside the test before you start deleting rows by hand. An outside process, including a queue worker started in another terminal, will not see the invoice either: the test never committed it.

DatabaseMigrations rebuilds the schema for every test. RefreshDatabase migrates once and wraps each test in a transaction. Prefer the transaction. A suite that migrates from scratch each time is slower and no cleaner, because the rollback already gives the next test an empty invoices table.

Recap

You can give each test an empty database without touching the one the browser uses.

  • RefreshDatabase migrates the testing database, then rolls each test back.
  • A worker outside the test cannot see the invoice, because it was never committed.
  • If a test sees a row it did not create, it is missing the trait or it is on the wrong database.

Check yourself · 3 questions

Question 1 of 3A queue worker you started in another terminal does not see the invoice the test created. Why?

Each test starts from an empty database. Next, name the invoice instead of repeating its columns.

← PreviousArrange, act, assert

On this page

  • Point the tests at their own database
  • The testing connection is a different database
  • 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.