ralbatross
ralbatross

Reputation: 2488

How Do I Seed My Database in the setupBeforeClass Method in a Laravel 4 Unit Test?

I have a Laravel 4 test class with some tests for which I want to seed my database before running the tests. Using the setup() function to reseed for each test takes far too long. However, when I try seeding in the static setupBeforeClass() function or the constructor, I obviously can't use the $this->seed() method.

But neither can I use programmatic Artisan commands, because when I do, I get the following error: PHP Fatal error: Class 'Artisan' not found in <test class name>.

Here is the code I'd like to use to seed:

Artisan::call('migrate:refresh');
Artisan::call('db:seed', array('--class'=>'TestSeeder');

Please let me know how I can seed my database once per test class rather than per test case

Upvotes: 15

Views: 11003

Answers (6)

ns16
ns16

Reputation: 1550

In Laravel 10 you don't need the setUpBeforeClass method for this. Instead you can use the RefreshDatabase trait. Before running the first test, it will rollback and re-run all the migrations and, if in the test class the $seed = true property is specified, will run the seeders. In addition, each test will be executed inside a transaction. For example:

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\CreatesApplication;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use CreatesApplication, RefreshDatabase;

    protected $seed = true;

    public function test_something(): void
    {
        // some code...
    }
}

See for details Running Seeders section and source code of the RefreshDatabase trait.

Upvotes: -1

Jerry
Jerry

Reputation: 666

Update: Laravel 5,6,7,8,9 https://laravel.com/docs/9.x/database-testing

use RefreshDatabase;

Outdated:

This trait is a wonderful way of resetting the database

<?php
namespace Tests;

use Illuminate\Support\Facades\Artisan;

trait MigrateFreshAndSeedOnce
{
    /**
     * If true, setup has run at least once.
     * @var boolean
     */
    protected static $setUpHasRunOnce = false;

    /**
     * After the first run of setUp "migrate:fresh --seed"
     * @return void
     */
    public function setUp() : void
    {
        parent::setUp();
        if (!static::$setUpHasRunOnce) {
            Artisan::call('migrate:fresh');
            Artisan::call(
                'db:seed',
                ['--class' => 'CompleteTestDbSeeder'] //add your seed class
            );
            static::$setUpHasRunOnce = true;
        }
    }
}

Upvotes: 1

Adrian Ocneanu
Adrian Ocneanu

Reputation: 1

You can do now:

protected function setUp()
{
    parent::setUp();

    $this->seed();
}

in your setUp() method for your test.

The seed() method accepts as a parameter the seeder class.

public function seed($class = 'DatabaseSeeder')
{
    $this->artisan('db:seed', ['--class' => $class]);

    return $this;
}

Upvotes: 0

Taskeen Fatima
Taskeen Fatima

Reputation: 61

This is so far the best solution I found

class ExampleTest extends TestCase {
/**
 * This method is called before
 * any test of TestCase class executed
 * @return void
 */
public static function setUpBeforeClass()
{
    parent::setUpBeforeClass();
    print "\nSETTING UP DATABASE\n";
    shell_exec('php artisan migrate --seed');
}

/**
 * This method is called after
 * all tests of TestCase class executed
 * @return void
 */
public static function tearDownAfterClass()
{
    shell_exec('php artisan migrate:reset');
    print "\nDESTROYED DATABASE\n";
    parent::tearDownAfterClass();
}
/** tests goes here **/ }

Upvotes: 6

NeuronQ
NeuronQ

Reputation: 8195

An "improvised" but pretty clean imho way to achieve a similar effect would be to do this in setUp, but have it run only once (similar to what setupBeforeClass does) like this:

use Illuminate\Support\Facades\Artisan;

class ExampleTest extends TestCase {

    protected static $db_inited = false;

    protected static function initDB()
    {
        echo "\n---initDB---\n"; // proof it only runs once per test TestCase class
        Artisan::call('migrate');
        // ...more db init stuff, like seeding etc.
    }

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

        if (!static::$db_inited) {
            static::$db_inited = true;
            static::initDB();
        }
    }

    // ...tests go here...
}

...this is my solution and it seems simple enough and works fine, solving the performance problems of seeding and rebuilding the db structure before every test run. But remember, the "right" way to do testing, that gives you the greatest confidence your tests methods don't get subtly interdependent in bug-hiding ways, is to re-seed your db before every test method, so just put seeding code in plain setUp if you can afford the performance penalty (for my test cases, I couldn't afford it, but ymmv...).

Upvotes: 31

ThallisPHP
ThallisPHP

Reputation: 319

I had the same problem and solved with this

passthru('cd ' . __DIR__ . '/../.. & php artisan migrate:refresh & db:seed --class=TestSeeder');

Upvotes: 5

Related Questions