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

bor1904's avatar

One time migrations and one time few seeders for tests

Hello, I have some problem with migrating and seeding my DB under testing process - phpunit.

Normally under development process we need a large amount of data in our DB so we have in project over 70 seeders to providing dummy data but for automatic testing we need only 5-6 seeders with sets of data like locations (4 sec needed), settings, predefined alerts and so on.

For tests we have addiitonal DB and for now we do this in steps manually from command line and/or enabling/disabling use DatabaseTransactions (disable trans -> manually run few seeders, enable trans ->run tests)

But we dreaming about some pro and automated solution where always we can run "php artisan test" and everything will configure just in time (migration + few seeders) and then runs tons of our tests :)

Any suggestion will be helpful!

0 likes
4 replies
Tray2's avatar

Do you really need to seed all those tables for one test?

What I do is that a only create the data I need for that test.

It can look something like this

class ShowTest extends TestCase
{
    use RefreshDatabase;
    /**
     * @test
     */
    public function it_shows_a_book(): void
    {

        $book = Book::factory([
            'title' => 'The Eye Of The World',
            'part' => 1,
            'pages' => 780,
            'published' => 1990,
            'blurb' => 'The wheel weaves as the wheel wills',
        ])
            ->has(Author::factory(['first_name' => 'Robert', 'last_name' => 'Jordan']))
            ->for(Serie::factory(['serie' => 'The Wheel Of Time', 'start_year' => 1990]))
            ->for(Edition::factory(['edition' => 'First Edition']))
            ->for(Publisher::factory(['publisher' => 'Tor']))
            ->for(Format::factory(['format' => 'Hardcover']))
            ->for(Genre::factory(['genre' => 'Fantasy']))
            ->for(Condition::factory(['condition' => 'Mint']))
            ->create();

        $this->get(action([BooksController::class, 'show'], $book->id))
            ->assertStatus(200)
            ->assertSeeText('Jordan, Robert')
            ->assertSeeText('The Eye Of The World')
            ->assertSeeText(1)
            ->assertSeeText(780)
            ->assertSeeText(1990)
            ->assertSeeText('First Edition')
            ->assertSeeText('Tor')
            ->assertSeeText('Hardcover')
            ->assertSeeText('Fantasy')
            ->assertSeeText('Mint')
            ->assertSeeText('The wheel weaves as the wheel wills');
    }
```
bor1904's avatar

No, I have opposite needs. I want to seed one time for serie of tests. If I want to after whole day run 200 tests then I want to write php artisan test and go for coffe (without any manual additional commands). And additionally I want to start on fresh DB and each test should run as separated transaction. Last one need is if I programming in TDD style I dont want run new seeders but just tests to accelerate speed.

I read many topics on laracasts and stackoverflow and prepare mechanism for this placed in TestCase (maybe helps somebody)

    static $migrated = false;

    public static function setUpBeforeClass():void
    {
        parent::setUpBeforeClass();
        if (!self::$migrated && 0) {
            passthru('cd ' . __DIR__ . '/../.. & php artisan migrate:fresh --database=testing');

            passthru('cd ' . __DIR__ . '/../.. & php artisan db:seed --database=testing --class=PermissionsTableSeeder');
            passthru('cd ' . __DIR__ . '/../.. & php artisan db:seed --database=testing --class=PaymentMethodTableSeeder');
            passthru('cd ' . __DIR__ . '/../.. & php artisan db:seed --database=testing --class=CustomNotificationTableSeeder');
            passthru('cd ' . __DIR__ . '/../.. & php artisan db:seed --database=testing --class=PopupTableSeeder');

            self::$migrated = true;
        }
    }

Explanation:

Function run first of all and avoid transaction mechanism (data isnt rolled back) Field migrate is used to avoid do this part of code in every test (only one time) 0 (zero) in statment is used to swith mode - 0 -develop&testing -1- tests on fresh (migrated and seeded) DB

Tray2's avatar

I think you are going about it the wrong way.

In my opinion you should

  1. Start the test
    1. Refresh the database
    2. Create the necessary records for the test.
    3. Perform the action
    4. Assert the result
  2. Start the next test
    1. Refresh the database
    2. Create the necessary records for the test.
    3. Perform the action
    4. Assert the result
  3. Repeat the process for all tests.
bor1904's avatar

Why my way is wrong?

System has few tables with "static" data like paymentMethods, statuses and so on. I seed them once and all tests are inside transactions.

Maybe you are right but I dont see advantages of your solution in relation to my ... (my is 100x faster because normally one test in my system = 0,2s , one DB refresh = 3-4s) + in many places code is less clean because before I assert something I need to create 5-10 objects which are background in most of cases.

Please don't think that I am some ignorant. I appreciate your vast experience and knowledge. I just don't like to do things just because someone wrote it :)

Please or to participate in this conversation.