Тестирование базы данных

Содержание:


Введение

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

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

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

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

Метод assertDatabaseHas и другие помощники как этот реализованы для удобства. Вы легко можете использовать метод утверждений PHPUnit для проведения ваших тестов.


Генерация фабрик

Для создания фабрики, используйте команду Artisan make:factory:

php artisan make:factory PostFactory

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

Опция --model может быть использована, чтобы показать имя модели созданной фабрикой. Эта опция также предварительно наполнит сгенерированный файл данными выбранной модели:

php artisan make:factory PostFactory --model=Post

Перезапуск базы данны после каждого теста

Часто бывает полезно переопределить вашу базу данных после каждого теста так, чтобы данные предыдущего теста никак ни влиаяли на предстоящие тесты. Трейт RefreshDatabase предоставляет наиболее оптимальный подход для работы с миграциями вашей базы данных в зависимости от типа базы: традиционная или база в оперативной памяти. Используя данный трейт для вашего класса теста все будет решено за вас:

<?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('/');

        // ...
    }
}

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

При тестированиии, вам может понадобиться вставить несколько записей в вашу базу данных до выполнения вашего теста. Вместо того, чтобы вручную указывать значение каждого столбца при создании тестовых данных, Ларавел позволяет вам определить базовый набор атрибутов для каждой модели Eloquent используя фабрики моделей. Для начала, давайте взглянем на файл database/factories/UserFactory.php в вашем приложении. В файле из коробке находится одно определение фабрики:

use Faker\Generator as Faker;
use Illuminate\Support\Str;

$factory->define(App\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
        'remember_token' => Str::random(10),
    ];
});

Внутри Замыкания, которое выступает как определение фабрики, вы можете возвращать базовые тестовые значения атрибутов для модели. Замыкание получит экземпляр библиотеки PHP Faker, что позволит вам удобно сгенерировать различные типы случайных данных для тестирования.

Вы также можете создать дополнительные файлы фабрик для каждой модели для лучшей организации. Например, вы можете создать файлы UserFactory.php и CommentFactory.php внутри вашей директрии database/factories. Все файлы внутри директории factories будут автоматически загружаться фреймворком Laravel.

Вы можете установить локаль Faker путём добавления опции faker_locale в ваш файл конфигурации config/app.php.

Фабрика состояний

Состояния позволяют вам определить отдельные модификации, которые могут быть применены к вашим фабрикам моделей в любых комбинациях. Например, ваша модель User должна иметь состояние delinquent, которое модифицирует одно из базовых значений атрибута. Вы можете определить ваши трансформации состояний используя метод state. Например, для простых состояних вы можете передать массив атрибутов модификаций:

$factory->state(App\User::class, 'delinquent', [
    'account_status' => 'delinquent',
]);

Если требуются некоторые вычисления или экземпляр $faker, вы можете использовать Замыкание, чтобы получить конечную модификацию атрибута:

$factory->state(App\User::class, 'address', function ($faker) {
    return [
        'address' => $faker->address,
    ];
});

Обратная связь фабрики

Обратная связь фабрик регистрируется с помощью методов afterMaking и afterCreating, позволяет вам реализовывать дополнительные задания после создания модели. Например, вы можете использовать обратную связь, чтобы связать дополнитлеьные модели с созданными моделями:

$factory->afterMaking(App\User::class, function ($user, $faker) {
    // ...
});

$factory->afterCreating(App\User::class, function ($user, $faker) {
    $user->accounts()->save(factory(App\Account::class)->make());
});

Вы также можете определить обратную связь для фабрики состояний:

$factory->afterMakingState(App\User::class, 'delinquent', function ($user, $faker) {
    // ...
});

$factory->afterCreatingState(App\User::class, 'delinquent', function ($user, $faker) {
    // ...
});

Использование фабрики

Создание моделей

После определения ваших фабрик, вы можете использовать глобальную функцию factory в ваших тестах или файлах сидерах для генерации экземпляров моделей. Поэтому, давайте взглянем на некоторые примеры создания моделей. Первое, мы будем использовать метод make для создания моделей, но сохранять их в базе данных:

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

    // Use model in tests...
}

Вы можете также создать коллекцию моделей или создать модели данного типа:

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

Применение состояний

Вы можете также применять любое из ваших состояний для моделей. Если вы хотите применить несколько трансформаций состояния для моделей, вы должны указать имя каждого состояния, которое вы и хотите применить:

$users = factory(App\User::class, 5)->states('delinquent')->make();

$users = factory(App\User::class, 5)->states('premium', 'delinquent')->make();

Перезапись атрибутов

Если вы хотите перезаписать некоторые базовые значения ваших моделей, вы можете передать массив значений в метод make. Ларавел заменит только указанные значения, в то время как остальные значения будут такими, как указано в фабрике:

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

Сохраняющиеся модели

Метод create не только создает экземпялр модели, но и еще сохраняет их в базу данных используя метод Eloquent save:

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

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

    // Use model in tests...
}

Вы можете переопределить атрибуты для модели путём передачи массива в метод create:

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

Отношения

В этом примере, мы прикрепим отношение к некоторым созданным моделям. При использовании метода create для создания нескольких моделей, Eloquent вернёт экземпляр коллекции, что позволяет вам использовать любые функции для работы с коллекциями, такие как each:

$users = factory(App\User::class, 3)
           ->create()
           ->each(function ($user) {
                $user->posts()->save(factory(App\Post::class)->make());
            });

Вы можете использовать метод createMany для создания нескольких связанных моделей:

$user->posts()->createMany(
    factory(App\Post::class, 3)->make()->toArray()
);

Отношения и Замыкания атрибутов

Вы можете также прикрепить связи к моделям используя замыкание атрибутов в определениях вашей фабрики. Например, если вы хотите создать новый экземпляр пользователя User при создании заметки Post, вы можете сделать следующее:

$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => function () {
            return factory(App\User::class)->create()->id;
        },
    ];
});

Такие Замыкания также получают рассчитанный массив атрибутов фабрики, которая их определяет:

$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => function () {
            return factory(App\User::class)->create()->id;
        },
        'user_type' => function (array $post) {
            return App\User::find($post['user_id'])->type;
        },
    ];
});

Использование наполнителей

Если вы хотите использовать наполнители базы данных для наполнения вашей базы данных в течение теста, вы можете исопльзовать метод seed. По умолчанию, метод seed вернёт DatabaseSeeder, который должен выполнить все ваши остальные наполнители. Как альтернативам, вы можете передать определённое имя класса наполнителя в метод seed:

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use OrderStatusesTableSeeder;
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(OrderStatusesTableSeeder::class);

        // ...
    }
}

Доступные утвержения

Ларавел предоставляет некоторые утверждения бд для ваших PHPUnit тестов:

Метод Описание
$this->assertDatabaseHas($table, array $data); Утверждает, что таблица в базе данных содержит указанные данные.
$this->assertDatabaseMissing($table, array $data); Утверждает, что таблица в базе данных не содержит указанные данные.
$this->assertSoftDeleted($table, array $data); Утверждает, что данная запись была мягко удалена.