How Do I Seed My Database in the setupBeforeClass

2020-08-17 07:36发布

问题:

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

回答1:

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



回答2:

I had the same problem and solved with this

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


回答3:

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 **/ }


回答4:

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


回答5:

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