Как выполнить миграцию базы данных для тестов laravel?

В документации Laravel рекомендуется использовать трейт DatabaseMigrations для миграции и отката базы данных между тестами.

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;

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

        // ...
    }
}

Однако у меня есть исходные данные, которые я хотел бы использовать в своих тестах. Если я бегу:

php artisan migrate --seed

затем он работает для первого теста, но не проходит последующие тесты. Это связано с тем, что трейт откатывает миграцию, и когда он снова запускает миграцию, он не заполняет базу данных. Как я могу запустить семена базы данных с миграцией?


person Jeff Puckett    schedule 20.02.2017    source источник


Ответы (7)


В Laravel 8, если вы используете черту RefreshDatabase, вы можете вызвать заполнение из своего тестового примера, используя ниже:

use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        // Run the DatabaseSeeder...
        $this->seed();

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

        $response = $this->get('/');

        // ...
    }
}

см. документы для получения дополнительной информации/примеров: https://laravel.com/docs/8.x/database-testing#running-seeders

person Sai    schedule 21.02.2021
comment
Именно то, что мне было нужно. Я думаю, что мои поиски в Google могли привести меня к более старым версиям. Благодарю вас! - person Nick; 03.03.2021

Все, что вам нужно сделать, это сделать ремесленный вызов db:seed в функции setUp.

<?php

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;

    public function setUp(): void
    {
        parent::setUp();

        // seed the database
        $this->artisan('db:seed');
        // alternatively you can call
        // $this->seed();
    }

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

        // ...
    }
}

ссылка: https://laravel.com/docs/5.6/testing#creating-and-running-tests

person lasec0203    schedule 15.04.2018
comment
Работал на меня. Спасибо. Ранее у меня сложилось неправильное впечатление, что setUp() запускается только один раз для каждого класса, а не для каждого теста. - person Hyder B.; 25.04.2018
comment
Есть ли способ сделать это на классном тесте, а не перед каждым тестом? Я попробовал setUpBeforeClass(), но это статическая функция, и я не могу раздавать и делать все, что мне нужно, из-за статической черты... Делать это в setUp() так медленно, когда вам нужно запустить кучу тестов, которые не требуют для полного сброса базы данных (и это плохо для модульного теста). - person SteamFire; 28.03.2019
comment
кроме того, вы можете вызвать $this->seed() в методе setUp(). - person Erich; 11.06.2019

Мне потребовалось некоторое время, чтобы понять это, поэтому я подумал Я бы поделился.

Если вы посмотрите на исходный код для DatabaseMigrations trait, то вы увидите, что у него есть одна функция runDatabaseMigrations, который вызывается setUp, который запускается перед каждым тестом и регистрирует обратный вызов для запуска при удалении.

Вы можете отсортировать "расширить" трейт с помощью псевдонима эту функцию, повторно объявите новую функцию с вашей логикой в ​​ней (artisan db:seed) под исходным именем и вызовите псевдоним внутри нее.

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
{
    use DatabaseMigrations {
        runDatabaseMigrations as baseRunDatabaseMigrations;
    }

    /**
     * Define hooks to migrate the database before and after each test.
     *
     * @return void
     */
    public function runDatabaseMigrations()
    {
        $this->baseRunDatabaseMigrations();
        $this->artisan('db:seed');
    }

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

        // ...
    }
}
person Jeff Puckett    schedule 20.02.2017
comment
Это должно быть в документации по тестированию! Заполнение может быть действительно важной частью тестирования, и я не видел упоминания об этом. поправьте меня если я ошибаюсь. - person Adam Menczykowski; 22.05.2017
comment
отличный ответ. Вот ссылка на документацию для тех, кому интересно, как создать свою сеялку: laravel.com/docs/5.6/seeding< /а> - person JP Lew; 10.03.2018
comment
Я оценил креативность здесь, но в итоге мои тесты заняли слишком много времени. (github.com/ghsukumar/SFDC_Best_Practices/wiki/ интересно.) Сейчас я работаю над тем, чтобы мои функциональные тесты были пустыми и повторно заполняли базу данных только один раз в начале набора тестов. (И используйте Sqlite вместо MySql.) - person Ryan; 10.05.2018
comment
@Jeff Pucker Мне пришлось использовать shell_exec('php artisan db:seed');, твоя линия $this->artisan('db:seed'); у меня не сработала. Но это отличное решение - person FosAvance; 15.08.2018
comment
Этот отличный подход позволяет нам выбирать тесты, требующие миграции и заполнения базы данных, в одном тестовом примере, используя простое условие if (in_array($this->getName(), $this->testsUsingDatabase)) ... внутри переопределенного runDatabaseMigrations(). (Здесь член класса $this->testsUsingDatabase должен быть массивом имен тестов, определенных разработчиком) - person Prisacari Dmitrii; 28.08.2018

Я знаю, что на этот вопрос уже отвечали несколько раз, но я не видел этого конкретного ответа, поэтому решил добавить его.

Некоторое время в laravel (по крайней мере, начиная с версии 5.5) в классе TestCase был метод, специально используемый для вызова сеялки базы данных:

https://laravel.com/api/5.7/Illuminate/Foundation/Testing/TestCase.html#method_seed

с этим методом вам просто нужно вызвать $this->seed('MySeederName'); для запуска сеялки.

Итак, если вы хотите, чтобы эта сеялка срабатывала перед каждым тестом, вы можете добавить следующую функцию setUp в свой тестовый класс:

public function setUp()
{
    parent::setUp();
    $this->seed('MySeederName');
}

Конечный результат такой же, как:

 $this->artisan('db:seed',['--class' => 'MySeederName'])

or

Artisan::call('db:seed', ['--class' => 'MySeederName'])

Но синтаксис немного чище (на мой взгляд).

person Chris Schmitz    schedule 12.12.2018
comment
Это самое чистое, что я видел, что еще нужно, кроме $this->seed('RolesTableSeeder') - person MeMReS; 30.03.2020

Если вы используете признак тестирования RefreshDatabase:

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication, RefreshDatabase {
        refreshDatabase as baseRefreshDatabase;
    }

    public function refreshDatabase()
    {
        $this->baseRefreshDatabase();

        // Seed the database on every database refresh.
        $this->artisan('db:seed');
    }
}
person Steve Bauman    schedule 14.02.2019

В Laravel 8 RefreshDatabase теперь ищет логическое свойство, называемое seed.

    /** 
     * Illuminate\Foundation\Testing\RefreshDatabase
     * Determine if the seed task should be run when refreshing the database.
     *
     * @return bool
     */
    protected function shouldSeed()
    {
        return property_exists($this, 'seed') ? $this->seed : false;
    }

Просто дайте вашему тестовому классу защищенное свойство $seed и установите для него значение true, если вы хотите засеять.

class ProjectControllerTest extends TestCase
{

    protected $seed = true;
    public function testCreateProject()
    {
        $project = Project::InRandomOrder()->first();
        $this->assertInstanceOf($project,Project::class);
    }

Хорошая часть этого метода заключается в том, что отдельные тесты не будут запускаться каждый раз при их запуске. Только начальный необходимый тест создаст базу данных.

person Jordan Casey    schedule 09.03.2021

Вот альтернативное решение на случай, если вы предпочитаете обходить родные Artisan DatabaseMigrations и методы раздачи/миграции. Вы можете создать свой собственный трейт для заполнения вашей базы данных:

namespace App\Traits;

use App\Models\User;
use App\Models\UserType;

trait DatabaseSetup 
{

    public function seedDatabase()
    {
        $user = $this->createUser();
    }

    public function createUser()
    {
        return factory(User::class)->create([
            'user_type_id' => function () {
                return factory(UserType::class)->create()->id;
            }
        ]);
    }

    public function getVar() {
        return 'My Data';
    }
}

Затем вызовите его в своем тесте следующим образом:

use App\Traits\DatabaseSetup;

class MyAwesomeTest extends TestCase
{
    use DatabaseSetup;
    use DatabaseTransactions;

    protected $reusableVar;

    public function setUp()
    {
        parent::setUp();
        $this->seedDatabase();
        $this->reusableVar = $this->getVar();
    }

    /**
     * @test
     */
    public function test_if_it_is_working()
    {
        $anotherUser = $this->createUser();
        $response = $this->get('/');
        $this->seeStatusCode(200);
    }

}
person JP Lew    schedule 09.03.2018