Summer Sale! All accounts are 50% off this week.

nztim's avatar
Level 5

Seeding the database for all tests (5.5)

I'm trying out the new RefreshDatabase trait with a test class where I want to have a default user in the database for each test without having to run factory() in every method.

I can't get anything to work using a @before. The only way I found that works is to override the refreshTestDatabase() method in RefreshDatabase and add a line that seeds.

Am I missing something here? Is there not a better way to do this?

0 likes
6 replies
Vasyanya's avatar

I use:

<?php

namespace Tests;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication, RefreshDatabase;

    protected function setUp()
    {
        parent::setUp();
        $this->artisan("db:seed");
    }
}
6 likes
nztim's avatar
Level 5

OK so RefreshDatabase will zero out everything and setUp() will add the seeds before each test? That's definitely better than what I had, thank you :)

I was however hoping that if RefreshDatabase is using transactions, it might as well roll back to a seeded state, rather than having to add them before each test. But if it can't do that then this is the next best thing.

1 like
haiko's avatar

Just wanted to add this for people who are using 5.8 like me, I needed to add : void to the setUp method.

    protected function setUp(): void
    {
        parent::setUp();
        $this->artisan("db:seed");
    }
hexdev's avatar

This seems to seed before every single test, which is perhaps not ideal.

It was mentioned in the original answer, that we could override the refreshTestDatabase method to add in a seed. That's what I do, and it means it will seed just once, right after the first set of migrations.

Depending on the amount of seeders you have, this can be a big performance gain for your tests.

<?php

namespace Tests\Traits;

use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\RefreshDatabaseState;

trait RefreshDatabaseWithSeeds
{
    use RefreshDatabase;

    /**
     * Refresh the in-memory database.
     *
     * @return void
     */
    protected function refreshInMemoryDatabase()
    {
        $this->artisan('migrate');

        $this->artisan('db:seed');

        $this->app[Kernel::class]->setArtisan(null);
    }

    /**
     * Refresh a conventional test database.
     *
     * @return void
     */
    protected function refreshTestDatabase()
    {
        if (! RefreshDatabaseState::$migrated) {
            $this->artisan('migrate:fresh', $this->shouldDropViews() ? [
                '--drop-views' => true,
            ] : []);

            $this->artisan('db:seed');

            $this->app[Kernel::class]->setArtisan(null);

            RefreshDatabaseState::$migrated = true;
        }

        $this->beginDatabaseTransaction();
    }
}

This will allow you to use RefreshDatabaseWithSeeds; in your extended TestCase, instead of use RefreshDatabase, and will therefore run the seeders only once.

<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Tests\Traits\RefreshDatabaseWithSeeds;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication, RefreshDatabaseWithSeeds;
}
3 likes
Rony@Bevol's avatar
<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Tests\Traits\RefreshDatabaseWithSeeds;

abstract class TestCase extends BaseTestCase
{
     use RefreshDatabase;

    protected function shouldSeed()
    {
        return true;
    }
}
onlime's avatar

or even shorter, just set the $seed property:

<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Tests\Traits\RefreshDatabaseWithSeeds;

abstract class TestCase extends BaseTestCase
{
     use RefreshDatabase;

    protected bool $seed = true;
}

it will be picked up by RefreshDatabase (CanConfigureMigrationCommands) trait.

2 likes

Please or to participate in this conversation.