46

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?

Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
  • You can run this->seed() for all seeders and $this->seed(YourClass::class) for specific seeder. Details in following laravel documentation.. https://laravel.com/docs/7.x/database-testing#using-seeds – Tuncay Elvanagac Aug 13 '22 at 12:23

7 Answers7

45

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

lasec0203
  • 2,422
  • 1
  • 21
  • 36
  • Worked for me. Thanks. I had the wrong impression previously that setUp() runs only one time per class and not per test. – Hyder B. Apr 25 '18 at 07:07
  • 4
    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). – SteamFire Mar 28 '19 at 08:53
  • 3
    additionally, you can call `$this->seed()` in the `setUp()` method. – Erich Jun 11 '19 at 18:53
36

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

Sai
  • 539
  • 6
  • 7
  • Exactly what I needed. I think my google searches might have been taking me to older versions. Thank you! – Nick Mar 03 '21 at 00:56
  • 7
    You can also use `protected bool $seed = true;` to seed once before all the tests – Tofandel Mar 31 '22 at 16:56
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('/');

        // ...
    }
}
Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
  • 12
    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. – Adam Menczykowski May 22 '17 at 10:39
  • great answer. Here's shortcut to the docs for anyone curious how to create your seeder: https://laravel.com/docs/5.6/seeding – JP Lew Mar 09 '18 at 22:11
  • 1
    I appreciated the creativity here, but it ended up making my tests take way too long. (https://github.com/ghsukumar/SFDC_Best_Practices/wiki/F.I.R.S.T-Principles-of-Unit-Testing 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.) – Ryan May 10 '18 at 12:50
  • @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 – FosAvance Aug 15 '18 at 15:56
  • 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) – Prisacari Dmitrii Aug 28 '18 at 10:20
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).

Chris Schmitz
  • 20,160
  • 30
  • 81
  • 137
  • That's the cleanest I've seen, what more do you need than `$this->seed('RolesTableSeeder')` – MeMReS Mar 29 '20 at 23:07
13

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.

Jordan Casey
  • 955
  • 9
  • 16
  • 1
    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 – Aitor Aug 16 '21 at 07:19
5

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');
    }
}
Steve Bauman
  • 8,165
  • 7
  • 40
  • 56
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);
    }

}
JP Lew
  • 4,121
  • 2
  • 32
  • 45