Версія фреймворка: 8.x

Тестування бази даних

Вступ

Laravel пропонує безліч корисних інструментів для спрощення тестування програм, керованих базами даних. По-перше, ви можете використовуватиassertDatabaseHasпомічник стверджувати, що дані існують у базі даних, що відповідають заданому набору критеріїв. Наприклад, якщо ви хочете перевірити, чи є запис у файліusersтаблиця зemailзначенняsally@example.com, ви можете зробити наступне:

public function testDatabase()
{
    // Make call to application...

    $this->assertDatabaseHas('users', [
        'email' => 'sally@example.com',
    ]);
}

Ви також можете використовуватиassertDatabaseMissingпомічник стверджувати, що дані не існують у базі даних.

assertDatabaseHasметод та інші подібні помічники для зручності. Ви можете використовувати будь-який із вбудованих методів твердження PHPUnit для доповнення тестів функцій.

Скидання бази даних після кожного тесту

Часто корисно скидати базу даних після кожного тесту, щоб дані попереднього тесту не перешкоджали подальшим тестам.RefreshDatabasetrait використовує найбільш оптимальний підхід до міграції тестової бази даних, залежно від того, використовуєте ви базу даних в пам'яті або традиційну базу даних. Використовуйте ознаку у своєму тестовому класі, і все буде оброблено для вас:

<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    use RefreshDatabase;

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

        // ...
    }
}

Створення фабрик

Під час тестування вам може знадобитися вставити кілька записів у вашу базу даних перед виконанням тесту. Замість того, щоб вручну вказувати значення кожного стовпця під час створення цих тестових даних, Laravel дозволяє визначити набір атрибутів за замовчуванням для кожного з вашихEloquent моделіза допомогою модельних фабрик.

Щоб створити фабрику, використовуйтеmake:factoryartisan командування:

php artisan make:factory PostFactory

Нова фабрика буде розміщена у вашомуdatabase/factoriesкаталог.

--modelОпція може використовуватися для Tagging назви моделі, створеної на Factory. Цей параметр попередньо заповнить згенерований Factoryький файл заданою моделлю:

php artisan make:factory PostFactory --model=Post

Написання фабрик

Для початку погляньте наdatabase/factories/UserFactory.phpфайл у вашій заявці. Зразу, цей файл містить таке Factoryьке визначення:

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = User::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'name' => $this->faker->name,
            'email' => $this->faker->unique()->safeEmail,
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
}

Як бачимо, у найосновнішому вигляді фабрики - це класи, які розширюють базовий Factoryький клас Laravel і визначають amodelмайно іdefinitionметод.definitionМетод повертає набір значень атрибутів за замовчуванням, які слід застосовувати при створенні моделі за допомогою Factory.

Черезfakerвласності, фабрики мають доступ доФакерБібліотека PHP, яка дозволяє зручно генерувати різні види випадкових даних для тестування.

Ви можете встановити локаль Faker, додавши afaker_localeваріант для вашогоconfig/app.phpфайл конфігурації.

Factory States

Методи державного маніпулювання дозволяють визначити дискретні модифікації, які можна застосувати до ваших модельних Factories у будь-якій комбінації. Наприклад, вашUserмодель може матиsuspendedстан, який змінює одне зі значень атрибута за замовчуванням. Ви можете визначити свої перетворення стану, використовуючи базові Factorystateметод. Ви можете називати свій метод стану як завгодно. Зрештою, це лише типовий метод PHP. Наданий зворотний виклик маніпуляції станом отримає масив необроблених атрибутів, визначених для Factory, і повинен повернути масив атрибутів для модифікації:

/**
 * Indicate that the user is suspended.
 *
 * @return \Illuminate\Database\Eloquent\Factories\Factory
 */
public function suspended()
{
    return $this->state(function (array $attributes) {
        return [
            'account_status' => 'suspended',
        ];
    });
}

Factory Callbacks

Factory Callbacks реєструються за допомогоюafterMakingіafterCreatingметоди і дозволяють виконувати додаткові завдання після виготовлення або створення моделі. Вам слід зареєструвати ці Callbacks, визначивши aconfigureметод на Factoryькому класі. Цей метод буде автоматично викликаний Laravel, коли буде створено екземпляр Factory:

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = User::class;

    /**
     * Configure the model factory.
     *
     * @return $this
     */
    public function configure()
    {
        return $this->afterMaking(function (User $user) {
            //
        })->afterCreating(function (User $user) {
            //
        });
    }

    // ...
}

Використання Factories

Створення моделей

Визначивши свої Factory, ви можете використовувати статикуfactoryметод, передбаченийIlluminate\Database\Eloquent\Factories\HasFactoryриси на ваших Eloquent моделях, щоб створити екземпляр Factoryького екземпляра для цієї моделі:

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasFactory;
}

Давайте розглянемо кілька прикладів створення моделей. Спочатку ми використаємоmakeметод створення моделей без збереження їх у базі даних:

use App\Models\User;

public function testDatabase()
{
    $user = User::factory()->make();

    // Use model in tests...
}

You may create a collection of many models using the countметод:

// Create three App\Models\User instances...
$users = User::factory()->count(3)->make();

HasFactoryособливостіfactoryМетод буде використовувати конвенції для визначення належної фабрики для моделі. Зокрема, метод буде шукати фабрику вDatabase\Factoriesпростір імен, який має ім'я класу, що відповідає імені моделі і суфіксом якого єFactory. Якщо ці умови не стосуються конкретного додатка чи Factory, ви можете замінитиnewFactoryметод на вашій моделі, щоб повернути екземпляр відповідного Factory моделі безпосередньо:

/**
 * Create a new factory instance for the model.
 *
 * @return \Illuminate\Database\Eloquent\Factories\Factory
 */
protected static function newFactory()
{
    return \Database\Factories\Administration\FlightFactory::new();
}

Держави, що застосовують

Ви також можете застосувати будь-який із своїхштатівдо моделей. Якщо ви хочете застосувати до моделей кілька перетворень стану, ви можете просто викликати методи стану безпосередньо:

$users = User::factory()->count(5)->suspended()->make();

Заміна атрибутів

Якщо ви хочете замінити деякі значення за замовчуванням для своїх моделей, ви можете передати масив значень вmakeметод. Тільки зазначені значення будуть замінені, тоді як для решти значень залишаються встановлені значення за замовчуванням, як це вказано на Factory:

$user = User::factory()->make([
    'name' => 'Abigail Otwell',
]);

Крім того,stateметод може бути викликаний безпосередньо на Factoryькому екземплярі для виконання вбудованого перетворення стану:

$user = User::factory()->state([
    'name' => 'Abigail Otwell',
])->make();
Захист від масового призначенняавтоматично вимикається при створенні моделей на Factoryх.

Персистуючі моделі

create method creates model instances and persists them to the database using Eloquent's saveметод:

use App\Models\User;

public function testDatabase()
{
    // Create a single App\Models\User instance...
    $user = User::factory()->create();

    // Create three App\Models\User instances...
    $users = User::factory()->count(3)->create();

    // Use model in tests...
}

Ви можете замінити атрибути на моделі, передавши масив атрибутів вcreateметод:

$user = User::factory()->create([
    'name' => 'Abigail',
]);

Послідовності

Іноді вам може знадобитися чергувати значення даного атрибута моделі для кожної створеної моделі. Ви можете досягти цього, визначивши перетворення держави якSequenceекземпляр. Наприклад, ми можемо побажати чергувати значенняadminстовпець на aUserмодель міжYіNдля кожного створеного користувача:

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
                ->count(10)
                ->state(new Sequence(
                    ['admin' => 'Y'],
                    ['admin' => 'N'],
                ))
                ->create();

У цьому прикладі буде створено п'ять користувачів ізadminзначенняYі п'ять користувачів будуть створені зadminзначенняN.

Factory зв'язки

Relationships Within Definitions

Ви можете прив’язати зв'язки до моделей у своїх Factoryьких визначеннях. Наприклад, якщо ви хочете створити новийUserпри створенніPost, ви можете зробити наступне:

use App\Models\User;

/**
 * Define the model's default state.
 *
 * @return array
 */
public function definition()
{
    return [
        'user_id' => User::factory(),
        'title' => $this->faker->title,
        'content' => $this->faker->paragraph,
    ];
}

Якщо стовпці відношення залежать від Factory, який його визначає, ви можете надати зворотний виклик, який приймає обчислюваний масив атрибутів:

/**
 * Define the model's default state.
 *
 * @return array
 */
public function definition()
{
    return [
        'user_id' => User::factory(),
        'user_type' => function (array $attributes) {
            return User::find($attributes['user_id'])->type;
        },
        'title' => $this->faker->title,
        'content' => $this->faker->paragraph,
    ];
}

Has Many Relationships

Далі, давайте дослідимо побудову Eloquent зв’язків між моделями, використовуючи вільні Factory методи Laravel. По-перше, припустимо, що наш додаток маєUserмодель та aPostмодель. Крім того, припустимо, щоUserмодель визначає ahasManyзв'язки зPost. Ми можемо створити користувача, який має три дописи за допомогоюhasметод, що надається Factory.hasметод приймає Factoryький екземпляр:

use App\Models\Post;
use App\Models\User;

$user = User::factory()
            ->has(Post::factory()->count(3))
            ->create();

By convention, when passing a Postмодель доhasметодом, Laravel припустить, щоUserмодель повинна мати apostsметод, що визначає взаємозв'язок. За необхідності ви можете чітко вказати назву зв'язків, якими ви хочете маніпулювати:

$user = User::factory()
            ->has(Post::factory()->count(3), 'posts')
            ->create();

Звичайно, ви можете виконувати державні маніпуляції на відповідних моделях. Крім того, ви можете пройти перетворення стану на основі закриття, якщо зміна стану вимагає доступу до батьківської моделі:

$user = User::factory()
            ->has(
                Post::factory()
                        ->count(3)
                        ->state(function (array $attributes, User $user) {
                            return ['user_type' => $user->type];
                        })
            )
            ->create();

Використання магічних методів

Для зручності ви можете використовувати Factory методи магічних зв'язків для визначення зв'язків. Наприклад, у наступному прикладі буде використано конвенцію, щоб визначити, що відповідні моделі слід створювати за допомогоюpostsметод зв'язків наUserмодель:

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

Використовуючи магічні методи для створення фабричних зв'язків, ви можете передати масив атрибутів, щоб замінити їх на відповідні моделі:

$user = User::factory()
            ->hasPosts(3, [
                'published' => false,
            ])
            ->create();

Ви можете надати перетворення стану на основі закриття, якщо зміна стану вимагає доступу до батьківської моделі:

$user = User::factory()
            ->hasPosts(3, function (array $attributes, User $user) {
                return ['user_type' => $user->type];
            })
            ->create();

Belongs To Relationships

Тепер, коли ми дослідили, як побудувати зв'язки "має багато" за допомогою фабрик, давайте дослідимо зворотний зв'язок.forметод може бути використаний для визначення моделі, якій належать створені на Factory моделі. Наприклад, ми можемо створити триPostекземпляри моделі, що належать одному користувачеві:

use App\Models\Post;
use App\Models\User;

$posts = Post::factory()
            ->count(3)
            ->for(User::factory()->state([
                'name' => 'Jessica Archer',
            ]))
            ->create();

Використання магічних методів

Для зручності ви можете використовувати Factory методи магічних зв'язків для визначення зв'язків "належить". Наприклад, у наступному прикладі буде використано конвенцію, щоб визначити, що три посади повинні належати доuserзв'язки наPostмодель:

$posts = Post::factory()
            ->count(3)
            ->forUser([
                'name' => 'Jessica Archer',
            ])
            ->create();

Багато до багатьох зв'язків

ЛюблюHas Many Relationships, зв'язки "багато до багатьох" можуть бути створені за допомогоюhasметод:

use App\Models\Role;
use App\Models\User;

$users = User::factory()
            ->has(Role::factory()->count(3))
            ->create();

Атрибути зведеної таблиці

Якщо вам потрібно визначити атрибути, які слід встановити у зведеній / проміжній таблиці, що пов'язує моделі, ви можете використовуватиhasAttachedметод. Цей метод приймає масив імен та значень атрибутів зведеної таблиці як другий аргумент:

use App\Models\Role;
use App\Models\User;

$users = User::factory()
            ->hasAttached(
                Role::factory()->count(3),
                ['active' => true]
            )
            ->create();

Ви можете надати перетворення стану на основі закриття, якщо зміна стану вимагає доступу до відповідної моделі:

$users = User::factory()
            ->hasAttached(
                Role::factory()
                    ->count(3)
                    ->state(function (array $attributes, User $user) {
                        return ['name' => $user->name.' Role'];
                    }),
                ['active' => true]
            )
            ->create();

Використання магічних методів

Для зручності ви можете використовувати Factory методи магічних зв'язків, щоб визначити багато-багато зв'язків. Наприклад, у наступному прикладі буде використано конвенцію, щоб визначити, що відповідні моделі слід створювати за допомогоюrolesметод зв'язків наUserмодель:

$users = User::factory()
            ->hasRoles(1, [
                'name' => 'Editor'
            ])
            ->create();

Поліморфні зв'язки

Поліморфні зв’язкитакож можуть бути створені за допомогою Factories. Поліморфні зв'язки "морф багато" створюються так само, як типові зв'язки "має багато". Наприклад, якщо aPostмодель маєmorphManyзв'язки з аCommentмодель:

use App\Models\Post;

$post = Post::factory()->hasComments(3)->create();

Морф до зв'язків

Магічні методи не можуть використовуватися для створенняmorphToзв'язки. Натомість,forметод повинен використовуватися безпосередньо, а назва зв'язків має бути чітко вказана. Наприклад, уявіть, щоCommentмодель маєcommentableметод, що визначає amorphToзв'язки. У цій ситуації ми можемо створити три коментарі, які належать до однієї публікації, використовуючиforметод безпосередньо:

$comments = Comment::factory()->count(3)->for(
    Post::factory(), 'commentable'
)->create();

Поліморфні зв'язки багато-багато-багато

Поліморфні зв'язки "багато-до-багатьох" можуть бути створені так само, як неполіморфні зв'язки "багато до багатьох":

use App\Models\Tag;
use App\Models\Video;

$videos = Video::factory()
            ->hasAttached(
                Tag::factory()->count(3),
                ['public' => true]
            )
            ->create();

Звичайно, магіяhasметод також може бути використаний для створення поліморфних зв'язків "багато до багатьох":

$videos = Video::factory()
            ->hasTags(3, ['public' => true])
            ->create();

Використання Seeds

Якщо ви хочете використовуватисівалки баз данихщоб заповнити базу даних під час тестування функцій, ви можете використовуватиseedметод. За замовчуваннямseedметод поверне файлDatabaseSeeder, який повинен стратити всіх інших ваших Seeders. Крім того, ви передаєте конкретну назву класу сівалки вseedметод:

<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    use RefreshDatabase;

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

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

        // ...
    }
}

Alternatively, you may instruct the RefreshDatabaseознака для автоматичного залучення бази даних перед кожним тестом. Ви можете досягти цього, визначивши a$seedвластивість у вашому тестовому класі:

<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    /**
     * Indicates whether the database should be seeded before each test.
     *
     * @var bool
     */
    protected $seed = true;
    
    // ...
}

ДоступніAssertions

Laravel пропонує кілька Assertions бази даних для вашогоPHPUnitтести функцій:

Метод Опис
$this->assertDatabaseCount($table, int $count); Стверджуйте, що таблиця в базі даних містить задану кількість записів.
$this->assertDatabaseHas($table, array $data); Стверджуйте, що таблиця в базі даних містить дані.
$this->assertDatabaseMissing($table, array $data); Стверджуйте, що таблиця в базі даних не містить даних.
$this->assertDeleted($table, array $data); Стверджуйте, що даний запис було видалено.
$this->assertSoftDeleted($table, array $data); Стверджуйте, що даний запис було видалено м’яко.

Для зручності ви можете передати модельassertDeletedіassertSoftDeletedПомічники для затвердження запису були видалені або видалені з бази даних відповідно на основі первинного ключа моделі.

Наприклад, якщо ви використовуєте фабрику моделей у своєму тесті, ви можете передати цю модель одному з таких помічників, щоб протестувати вашу програму, щоб правильно видалити запис із бази даних:

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

    // Make call to application...

    $this->assertDeleted($user);
}