49

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?

1

8 Answers 8

47

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

Sign up to request clarification or add additional context in comments.

4 Comments

Worked for me. Thanks. I had the wrong impression previously that setUp() runs only one time per class and not per test.
Is there a way to do this at class test and not before every tests? I tried the setUpBeforeClass() but it is a static function and I can't seed and do everything I need because of the static trait... Doing it at setUp() is so slow when you need to run a bunch of tests that doesn't require to reset the database fully (and it's bad for a unit test).
additionally, you can call $this->seed() in the setUp() method.
@SteamFire Since PHP unit lets you run individual test methods from the command line, this could yield unexpected results if the database is only seeded at the class level rather than before each method call.
41

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

2 Comments

Exactly what I needed. I think my google searches might have been taking me to older versions. Thank you!
You can also use protected bool $seed = true; to seed once before all the tests
27

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

        // ...
    }
}

5 Comments

This should be in the testing documentation! Seeding can be a really important part of testing and I didn't see any mention of this. correct me If I'm wrong.
great answer. Here's shortcut to the docs for anyone curious how to create your seeder: laravel.com/docs/5.6/seeding
I appreciated the creativity here, but it ended up making my tests take way too long. (github.com/ghsukumar/SFDC_Best_Practices/wiki/… is interesting.) I'm now working on making my functional tests empty and re-seed the database just once at the beginning of a test suite. (And use Sqlite instad of MySql.)
@Jeff Pucker I had to use shell_exec('php artisan db:seed');, your line $this->artisan('db:seed'); didn't work out for me. But this is awesome solution
This great approach allows us to choose the tests that require database migration and seeding within one test case by using simple condition if (in_array($this->getName(), $this->testsUsingDatabase)) ... inside overrided runDatabaseMigrations(). (Here class member $this->testsUsingDatabase should be an array of test names defined by developer)
18

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).

1 Comment

That's the cleanest I've seen, what more do you need than $this->seed('RolesTableSeeder')
15

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.

1 Comment

The setUp() function stopped working when I updated to php 8.0.9, getting an error in PDO rollback. The $seed property solved my error. Thanks
6

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

Comments

0

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

}

Comments

0

I will attribute this answer to Joel Clermont. You may refer to his response here: https://masteringlaravel.io/daily/2023-12-04-you-dont-need-to-manually-run-seeders-in-your-tests

The Laravel TestCase supports a $seed property. When you set this to true and also use the RefreshDatabase trait, it then automatically seeds your database using the default seeder.

This method is much faster as it only runs once. If you place the seed command in the setup method, it will run on every test.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.