Skip to content

資料庫測試

簡介

Laravel 提供了多種實用的工具和斷言,讓測試資料庫驅動的應用程式更加容易。此外,Laravel 的 Model Factories 和 seeder 讓您能夠輕鬆地使用應用程式的 Eloquent Model 和關聯來建立測試資料庫紀錄。我們將在接下來的文件中討論所有這些強大的功能。

每次測試後重置資料庫

在進一步討論之前,讓我們先探討如何在每次測試後重置資料庫,以避免前一個測試的資料干擾後續測試。Laravel 內建的 Illuminate\Foundation\Testing\RefreshDatabase trait 將為您處理這件事。只需在您的測試類別中使用該 trait 即可:

php
<?php

use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('basic example', function () {
    $response = $this->get('/');

    // ...
});
php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * A basic functional test example.
     */
    public function test_basic_example(): void
    {
        $response = $this->get('/');

        // ...
    }
}

Illuminate\Foundation\Testing\RefreshDatabase trait 不會在您的 schema 是最新狀態時遷移您的資料庫。相反地,它只會在資料庫交易中執行測試。因此,未曾使用此 trait 的測試案例所新增到資料庫的任何紀錄可能仍然存在於資料庫中。

如果您想完全重置資料庫,您可以改用 Illuminate\Foundation\Testing\DatabaseMigrationsIlluminate\Foundation\Testing\DatabaseTruncation traits。然而,這兩個選項都比 RefreshDatabase trait 慢得多。

Model Factories

進行測試時,您可能需要在執行測試前將一些紀錄插入到資料庫中。Laravel 允許您為每個 Eloquent Model 定義一組預設屬性,透過 Model Factories 來建立測試資料,而無需手動指定每個欄位的值。

要了解更多關於建立和利用 Model Factories 來建立 Model 的資訊,請查閱完整的 Model Factory 文件。一旦您定義了一個 Model Factory,您就可以在測試中利用該 factory 來建立 Model:

php
use App\Models\User;

test('models can be instantiated', function () {
    $user = User::factory()->create();

    // ...
});
php
use App\Models\User;

public function test_models_can_be_instantiated(): void
{
    $user = User::factory()->create();

    // ...
}

執行 Seeder

如果您想在功能測試期間使用 database seeder 來填充資料庫,您可以呼叫 seed 方法。預設情況下,seed 方法將執行 DatabaseSeeder,它應該會執行所有其他的 seeder。或者,您可以將特定的 seeder 類別名稱傳遞給 seed 方法:

php
<?php

use Database\Seeders\OrderStatusSeeder;
use Database\Seeders\TransactionStatusSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('orders can be created', function () {
    // Run the DatabaseSeeder...
    $this->seed();

    // Run a specific seeder...
    $this->seed(OrderStatusSeeder::class);

    // ...

    // Run an array of specific seeders...
    $this->seed([
        OrderStatusSeeder::class,
        TransactionStatusSeeder::class,
        // ...
    ]);
});
php
<?php

namespace Tests\Feature;

use Database\Seeders\OrderStatusSeeder;
use Database\Seeders\TransactionStatusSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Test creating a new order.
     */
    public function test_orders_can_be_created(): void
    {
        // Run the DatabaseSeeder...
        $this->seed();

        // Run a specific seeder...
        $this->seed(OrderStatusSeeder::class);

        // ...

        // Run an array of specific seeders...
        $this->seed([
            OrderStatusSeeder::class,
            TransactionStatusSeeder::class,
            // ...
        ]);
    }
}

另外,您可以指示 Laravel 在每次使用 RefreshDatabase trait 的測試之前自動填充資料庫。您可以透過在基礎測試類別上定義 $seed 屬性來實現此目的:

<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    /**
     * Indicates whether the default seeder should run before each test.
     *
     * @var bool
     */
    protected $seed = true;
}

$seed 屬性為 true 時,測試將在每次使用 RefreshDatabase trait 的測試之前執行 Database\Seeders\DatabaseSeeder 類別。然而,您可以透過在測試類別上定義 $seeder 屬性來指定應執行的特定 seeder:

use Database\Seeders\OrderStatusSeeder;

/**
 * Run a specific seeder before each test.
 *
 * @var string
 */
protected $seeder = OrderStatusSeeder::class;

可用的斷言

Laravel 為您的 PestPHPUnit 功能測試提供了多種資料庫斷言。我們將在下方討論每一個斷言。

assertDatabaseCount

斷言資料庫中的某個資料表包含指定數量的紀錄:

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

assertDatabaseEmpty

斷言資料庫中的某個資料表不包含任何紀錄:

$this->assertDatabaseEmpty('users');

assertDatabaseHas

斷言資料庫中的某個資料表包含符合指定鍵值查詢條件的紀錄:

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

assertDatabaseMissing

斷言資料庫中的某個資料表不包含符合指定鍵值查詢條件的紀錄:

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

assertSoftDeleted

assertSoftDeleted 方法可用於斷言指定的 Eloquent Model 已被「軟刪除」:

$this->assertSoftDeleted($user);

assertNotSoftDeleted

assertNotSoftDeleted 方法可用於斷言指定的 Eloquent Model 尚未被「軟刪除」:

$this->assertNotSoftDeleted($user);

assertModelExists

斷言指定的 Model 存在於資料庫中:

use App\Models\User;

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

$this->assertModelExists($user);

assertModelMissing

斷言指定的 Model 不存在於資料庫中:

use App\Models\User;

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

$user->delete();

$this->assertModelMissing($user);

expectsDatabaseQueryCount

expectsDatabaseQueryCount 方法可以在測試開始時呼叫,以指定您預期在測試期間執行的資料庫查詢總數。如果實際執行的查詢數量與此預期不完全相符,測試將會失敗:

$this->expectsDatabaseQueryCount(5);

// Test...