6

I am trying to set up tests in Laravel, but I want to run different migrations from those that usually run.

The migrations that I run to initiate the database imports data from a production environment.
For testing I want to use a different database called "test", and I want to fill this test database with test data, not production data.

I added a "testing" connection to config/database.php which uses the "test" database:

'connections' => [

        'mysql' => [
            'database' => env('DB_DATABASE', 'forge'),
            ...
        ],

        'testing' => [
            'database' => 'test',
            ...
        ],
],

And I setup phpunit.xml to use this "testing" connection:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit ...>
    ...
    <php>
        <env name="DB_CONNECTION" value="testing"/>
        ...
    </php>
</phpunit>

Now I want to initialize this "test" database with test data, using migrations from a different folder than the default.

I can use the normal migrations like this:

<?php

namespace Tests;

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

abstract class TestCase extends BaseTestCase
{
    use DatabaseMigrations;

    public function setUp(): void
    {
        parent::setUp();
        $this->seed();
    }
}

But this uses the default folder database/migrations. I would like to put the testing migrations in folder tests/database/migrations.

Is there a way to let use DatabaseMigrations; use migrations from another folder?

3 Answers 3

11

You might need to override runDatabaseMigrations method in DatabaseMigrations trait and set the app's database path from there before your migrations run.

The runDatabaseMigrations method might end up looking like this:

use DatabaseMigrations { runDatabaseMigrations as runMigration; }

    public function runDatabaseMigrations()
    {
        $this->app->useDatabasePath(base_path('tests/database')); //example path
//        dump($this->app->databasePath());
        $this->runMigration();
    }

Or you can set in the boot method of your AppService provider:

    if (config('app.env') === 'testing') { //Laravel automatically set env to 'testing' when running test
        $this->app->useDatabasePath(base_path('tests/database'));
    }

The migration will look for a sub-folder of 'tests/database' called "migrations".

PS: There will be side effect to this if you're have some other code or folder in the default database folder. Example, your factory class there will not be found for this test class.

Sign up to request clarification or add additional context in comments.

3 Comments

Two very elegant solutions, thank you. It took me a while to find out that the path in your code should be tests/database and not tests/database/migrations. The migration will look for a sub-folder called "migrations".
Apologies for that, I didn't read the question very well to know you needed tests/database I will update the answer to reflect this and also include your statement "The migration will look for a sub-folder called "migrations""
I took the general service provider approach since I have huge legacy project for which I want to keep developing but with tests. Turns out, with the newer class-based factories in Laravel 8 they work just fine in the test classes even though the path is overwritten since the factory classes are name-spaced and imported at the top of my test :) awesome!
1

Artisan migrate has a path option, you have to make your own trait to have similar functionality. I'm thinking something like this.

trait PathDatabaseMigrations {

    public function runDatabaseMigrations()
    {
        // optimal 
        $path = 'tests/database/migrations';

        $this->artisan('migrate:fresh', ['--path' => $path,]);

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

        $this->beforeApplicationDestroyed(function () {
            $this->artisan('migrate:rollback', ['--path' => $path,]);

            RefreshDatabaseState::$migrated = false;
        });
    }
}

Comments

1

The RefreshDatabase trait has a method called migrateUsing which will overload the parameters that it uses when running the migrations. I changed to a specific test migration file for a single test by passing it in as the --path parameter to the migration.

namespace Tests\Unit;

use Tests\TestCase;
use App\Models\Traits\HasUuid;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\RefreshDatabase;

class HasUuidTest extends TestCase
{
    use RefreshDatabase;

    protected function migrateUsing()
    {
        return [
            '--path' => 'tests/migrations/2022_03_15_220516_create_test_uuid_table.php'
        ];
    }

    public function test_has_uuid()
    {
        $model = new TestUuid;
        $this->assertEmpty($model->id);
        $model->save();
        $this->assertNotEmpty($model->id);
    }
}

class TestUuid extends Model
{
    use HasUuid;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.