Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 577
ralbatross Avatar asked Feb 19 '14 22:02

ralbatross


People also ask

What is DB seed in laravel?

Laravel includes the ability to seed your database with data using seed classes. All seed classes are stored in the database/seeders directory. By default, a DatabaseSeeder class is defined for you. From this class, you may use the call method to run other seed classes, allowing you to control the seeding order.

What is feature and unit test in laravel?

In Laravel, there are two ways to test your app. One is with Unit testing while other is with Feature testing. Unit testing allows to test your classes models, controllers etc, while Feature testing enables you to test your code base.


4 Answers

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

like image 159
NeuronQ Avatar answered Oct 11 '22 02:10

NeuronQ


I had the same problem and solved with this

passthru('cd ' . __DIR__ . '/../.. & php artisan migrate:refresh & db:seed --class=TestSeeder');
like image 37
ThallisPHP Avatar answered Oct 11 '22 01:10

ThallisPHP


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 **/ }
like image 27
Taskeen Fatima Avatar answered Oct 11 '22 00:10

Taskeen Fatima


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;
        }
    }
}
like image 39
DevJ3rry Avatar answered Oct 11 '22 02:10

DevJ3rry