Database Testing

Introduction

Laravel provides a variety of helpful tools and assertions to make it easier to test your database driven applications. In addition, Laravel model factories and seeders make it painless to create test database records using your application's Eloquent models and relationships. We'll discuss all of these powerful features in the following documentation.

Resetting the Database After Each Test

Before proceeding much further, let's discuss how to reset your database after each of your tests so that data from a previous test does not interfere with subsequent tests. Laravel's included Illuminate\Foundation\Testing\RefreshDatabase trait will take care of this for you. Simply use the trait on your test class:

1<?php
2 
3use Illuminate\Foundation\Testing\RefreshDatabase;
4 
5uses(RefreshDatabase::class);
6 
7test('basic example', function () {
8 $response = $this->get('/');
9 
10 // ...
11});
1<?php
2 
3use Illuminate\Foundation\Testing\RefreshDatabase;
4 
5uses(RefreshDatabase::class);
6 
7test('basic example', function () {
8 $response = $this->get('/');
9 
10 // ...
11});
1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Tests\TestCase;
7 
8class ExampleTest extends TestCase
9{
10 use RefreshDatabase;
11 
12 /**
13 * A basic functional test example.
14 */
15 public function test_basic_example(): void
16 {
17 $response = $this->get('/');
18 
19 // ...
20 }
21}
1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Tests\TestCase;
7 
8class ExampleTest extends TestCase
9{
10 use RefreshDatabase;
11 
12 /**
13 * A basic functional test example.
14 */
15 public function test_basic_example(): void
16 {
17 $response = $this->get('/');
18 
19 // ...
20 }
21}

The Illuminate\Foundation\Testing\RefreshDatabase trait does not migrate your database if your schema is up to date. Instead, it will only execute the test within a database transaction. Therefore, any records added to the database by test cases that do not use this trait may still exist in the database.

If you would like to totally reset the database, you may use the Illuminate\Foundation\Testing\DatabaseMigrations or Illuminate\Foundation\Testing\DatabaseTruncation traits instead. However, both of these options are significantly slower than the RefreshDatabase trait.

Model Factories

When testing, you may need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Laravel allows you to define a set of default attributes for each of your Eloquent models using model factories.

To learn more about creating and utilizing model factories to create models, please consult the complete model factory documentation. Once you have defined a model factory, you may utilize the factory within your test to create models:

1use App\Models\User;
2 
3test('models can be instantiated', function () {
4 $user = User::factory()->create();
5 
6 // ...
7});
1use App\Models\User;
2 
3test('models can be instantiated', function () {
4 $user = User::factory()->create();
5 
6 // ...
7});
1use App\Models\User;
2 
3public function test_models_can_be_instantiated(): void
4{
5 $user = User::factory()->create();
6 
7 // ...
8}
1use App\Models\User;
2 
3public function test_models_can_be_instantiated(): void
4{
5 $user = User::factory()->create();
6 
7 // ...
8}

Running Seeders

If you would like to use database seeders to populate your database during a feature test, you may invoke the seed method. By default, the seed method will execute the DatabaseSeeder, which should execute all of your other seeders. Alternatively, you pass a specific seeder class name to the seed method:

1<?php
2 
3use Database\Seeders\OrderStatusSeeder;
4use Database\Seeders\TransactionStatusSeeder;
5use Illuminate\Foundation\Testing\RefreshDatabase;
6 
7uses(RefreshDatabase::class);
8 
9test('orders can be created', function () {
10 // Run the DatabaseSeeder...
11 $this->seed();
12 
13 // Run a specific seeder...
14 $this->seed(OrderStatusSeeder::class);
15 
16 // ...
17 
18 // Run an array of specific seeders...
19 $this->seed([
20 OrderStatusSeeder::class,
21 TransactionStatusSeeder::class,
22 // ...
23 ]);
24});
1<?php
2 
3use Database\Seeders\OrderStatusSeeder;
4use Database\Seeders\TransactionStatusSeeder;
5use Illuminate\Foundation\Testing\RefreshDatabase;
6 
7uses(RefreshDatabase::class);
8 
9test('orders can be created', function () {
10 // Run the DatabaseSeeder...
11 $this->seed();
12 
13 // Run a specific seeder...
14 $this->seed(OrderStatusSeeder::class);
15 
16 // ...
17 
18 // Run an array of specific seeders...
19 $this->seed([
20 OrderStatusSeeder::class,
21 TransactionStatusSeeder::class,
22 // ...
23 ]);
24});
1<?php
2 
3namespace Tests\Feature;
4 
5use Database\Seeders\OrderStatusSeeder;
6use Database\Seeders\TransactionStatusSeeder;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Tests\TestCase;
9 
10class ExampleTest extends TestCase
11{
12 use RefreshDatabase;
13 
14 /**
15 * Test creating a new order.
16 */
17 public function test_orders_can_be_created(): void
18 {
19 // Run the DatabaseSeeder...
20 $this->seed();
21 
22 // Run a specific seeder...
23 $this->seed(OrderStatusSeeder::class);
24 
25 // ...
26 
27 // Run an array of specific seeders...
28 $this->seed([
29 OrderStatusSeeder::class,
30 TransactionStatusSeeder::class,
31 // ...
32 ]);
33 }
34}
1<?php
2 
3namespace Tests\Feature;
4 
5use Database\Seeders\OrderStatusSeeder;
6use Database\Seeders\TransactionStatusSeeder;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Tests\TestCase;
9 
10class ExampleTest extends TestCase
11{
12 use RefreshDatabase;
13 
14 /**
15 * Test creating a new order.
16 */
17 public function test_orders_can_be_created(): void
18 {
19 // Run the DatabaseSeeder...
20 $this->seed();
21 
22 // Run a specific seeder...
23 $this->seed(OrderStatusSeeder::class);
24 
25 // ...
26 
27 // Run an array of specific seeders...
28 $this->seed([
29 OrderStatusSeeder::class,
30 TransactionStatusSeeder::class,
31 // ...
32 ]);
33 }
34}

Alternatively, you may instruct Laravel to automatically seed the database before each test that uses the RefreshDatabase trait. You may accomplish this by defining a $seed property on your base test class:

1<?php
2 
3namespace Tests;
4 
5use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
6 
7abstract class TestCase extends BaseTestCase
8{
9 /**
10 * Indicates whether the default seeder should run before each test.
11 *
12 * @var bool
13 */
14 protected $seed = true;
15}
1<?php
2 
3namespace Tests;
4 
5use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
6 
7abstract class TestCase extends BaseTestCase
8{
9 /**
10 * Indicates whether the default seeder should run before each test.
11 *
12 * @var bool
13 */
14 protected $seed = true;
15}

When the $seed property is true, the test will run the Database\Seeders\DatabaseSeeder class before each test that uses the RefreshDatabase trait. However, you may specify a specific seeder that should be executed by defining a $seeder property on your test class:

1use Database\Seeders\OrderStatusSeeder;
2 
3/**
4 * Run a specific seeder before each test.
5 *
6 * @var string
7 */
8protected $seeder = OrderStatusSeeder::class;
1use Database\Seeders\OrderStatusSeeder;
2 
3/**
4 * Run a specific seeder before each test.
5 *
6 * @var string
7 */
8protected $seeder = OrderStatusSeeder::class;

Available Assertions

Laravel provides several database assertions for your Pest or PHPUnit feature tests. We'll discuss each of these assertions below.

assertDatabaseCount

Assert that a table in the database contains the given number of records:

1$this->assertDatabaseCount('users', 5);
1$this->assertDatabaseCount('users', 5);

assertDatabaseEmpty

Assert that a table in the database contains no records:

1$this->assertDatabaseEmpty('users');
1$this->assertDatabaseEmpty('users');

assertDatabaseHas

Assert that a table in the database contains records matching the given key / value query constraints:

1$this->assertDatabaseHas('users', [
2 'email' => '[email protected]',
3]);
1$this->assertDatabaseHas('users', [
2 'email' => '[email protected]',
3]);

assertDatabaseMissing

Assert that a table in the database does not contain records matching the given key / value query constraints:

1$this->assertDatabaseMissing('users', [
2 'email' => '[email protected]',
3]);
1$this->assertDatabaseMissing('users', [
2 'email' => '[email protected]',
3]);

assertSoftDeleted

The assertSoftDeleted method may be used to assert a given Eloquent model has been "soft deleted":

1$this->assertSoftDeleted($user);
1$this->assertSoftDeleted($user);

assertNotSoftDeleted

The assertNotSoftDeleted method may be used to assert a given Eloquent model hasn't been "soft deleted":

1$this->assertNotSoftDeleted($user);
1$this->assertNotSoftDeleted($user);

assertModelExists

Assert that a given model exists in the database:

1use App\Models\User;
2 
3$user = User::factory()->create();
4 
5$this->assertModelExists($user);
1use App\Models\User;
2 
3$user = User::factory()->create();
4 
5$this->assertModelExists($user);

assertModelMissing

Assert that a given model does not exist in the database:

1use App\Models\User;
2 
3$user = User::factory()->create();
4 
5$user->delete();
6 
7$this->assertModelMissing($user);
1use App\Models\User;
2 
3$user = User::factory()->create();
4 
5$user->delete();
6 
7$this->assertModelMissing($user);

expectsDatabaseQueryCount

The expectsDatabaseQueryCount method may be invoked at the beginning of your test to specify the total number of database queries that you expect to be run during the test. If the actual number of executed queries does not exactly match this expectation, the test will fail:

1$this->expectsDatabaseQueryCount(5);
2 
3// Test...
1$this->expectsDatabaseQueryCount(5);
2 
3// Test...

Comments

No Comments Yet

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.