简体   繁体   中英

How to seed database migrations for laravel tests?

Laravel's documentation recommends using the DatabaseMigrations trait for migrating and rolling back the database between tests.

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;

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

        // ...
    }
}

However, I've got some seed data that I would like to use with my tests. If I run:

php artisan migrate --seed

then it works for the first test, but it fails subsequent tests. This is because the trait rolls back the migration, and when it runs the migration again, it doesn't seed the database. How can I run the database seeds with the migration?

All you need to do is make an artisan call db:seed in the setUp function

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

        // ...
    }
}

ref:https://laravel.com/docs/5.6/testing#creating-and-running-tests

It took me some digging to figure this out, so I thought I'd share .

If you look at the source code for the DatabaseMigrations trait , then you'll see it has one function runDatabaseMigrations that's invoked by setUp which runs before every test and registers a callback to be run on teardown.

You can sort of "extend" the trait by aliasing that function, re-declare a new function with your logic in it ( artisan db:seed ) under the original name, and call the alias inside it.

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

        // ...
    }
}

I know this question has already been answered several times, but I didn't see this particular answer so I thought I'd throw it in.

For a while in laravel (at least since v5.5), there's been a method in the TestCase class specifically used for calling a database seeder:

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

with this method, you just need to call $this->seed('MySeederName'); to fire the seeder.

So if you want this seeder to fire before every test, you can add the following setUp function to your test class:

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

The end result is the same as:

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

or

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

But the syntax is a bit cleaner (in my opinion).

With Laravel 8, if you're using the RefreshDatabase trait you can invoke seeding from your test case using below:

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

        // ...
    }
}

see docs for more information/examples: https://laravel.com/docs/8.x/database-testing#running-seeders

If you're using the RefreshDatabase testing trait:

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');
    }
}

With Laravel 8, the RefreshDatabase is now looking for a boolean property called "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;
    }

Simply give your test class the protected property $seed and set it to true if you wish to seed.

class ProjectControllerTest extends TestCase
{

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

The nice part about this method is that individual tests won't seed everytime they are ran. Only seed necessary test will build the database.

Here is an alternate solution, in case you prefer to bypass Artisan's native DatabaseMigrations and seeder/migration methods. You can create your own trait to seed your database:

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';
    }
}

Then call it in your test like this:

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);
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM