Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

digitalhuman's avatar

Multi-database tests

Hi all,

So, we are really running out of options here. We use different database and some tests rely on these databases. We run the tests with RefreshDatabase trait and at first we expect it to refresh all 'databases' that are in use. We found out that this is not the case. So, we now 'refresh' the additional databases using the Artisan facade in the test where we actually use the additional database.

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

        Artisan::call("migrate:fresh --env=tests --path=database/migrations/db2 --database=db2");
    }

Now this does not work. We also tried moving this logic to the TestCase but nothing seem to work properly either. We are looking for a more controlled way to refresh databases but without loosing the ability to refresh the database only once for all tests.

I hope anyone here can give us some new leads or solutions.

0 likes
3 replies
LaryAI's avatar
Level 58

To handle multiple database testing with migrations in Laravel, you can create a custom trait that will refresh the databases you need. This trait can be used in place of or alongside the RefreshDatabase trait provided by Laravel.

Here's an example of how you might create a custom trait to handle multiple databases:

trait RefreshMultipleDatabases
{
    /**
     * Refresh the database connections before each test.
     */
    public function refreshDatabases()
    {
        $this->refreshDatabase();
        $this->refreshAdditionalDatabase('db2');
    }

    /**
     * Refresh the primary database.
     */
    protected function refreshDatabase()
    {
        $this->artisan('migrate:fresh', ['--database' => 'mysql']);
    }

    /**
     * Refresh an additional database.
     *
     * @param string $database
     */
    protected function refreshAdditionalDatabase($database)
    {
        $this->artisan('migrate:fresh', [
            '--database' => $database,
            '--path' => "database/migrations/{$database}",
        ]);
    }

    /**
     * Define hooks to migrate the databases before and after each test.
     */
    public function setUpTraits()
    {
        parent::setUpTraits();

        if (isset($this->uses[RefreshMultipleDatabases::class])) {
            $this->refreshDatabases();
        }
    }
}

You would then use this trait in your test classes like so:

class ExampleTest extends TestCase
{
    use RefreshMultipleDatabases;

    // Your test methods...
}

This trait overrides the setUpTraits method to ensure that the databases are refreshed before each test. It also provides methods to refresh the primary database and any additional databases you might have.

Please note that you may need to adjust the refreshAdditionalDatabase method to match the specific configuration of your additional databases, such as the correct migration path.

Also, be aware that running migrations for multiple databases can significantly slow down your test suite. If possible, try to minimize the number of tests that require refreshing multiple databases, or consider using transactions to roll back changes instead of refreshing the entire database after each test.

mike_isp's avatar

Not sure if I understand fully, but maybe the DatabaseTransactions trait is what you need. It'll wrap changes to the DB in transactions that are rolled back after the test so the DB isn't permanently modified.

You could use that instead of RefreshDatabase on the tests that interact with the DBs that have data you want to keep.

Alternatively, could you use a separate DBs, and use seeders or factories to create the data you need before running the tests?

cabsey's avatar

Apologies for reviving a fairly old post, but you can just set this property on your test class:

$this->connectionsToTransact = ['connection_a', 'connection_b'];

It works because in RefreshDatabase, this method exists:

protected function connectionsToTransact()
{
    return property_exists($this, 'connectionsToTransact')
                        ? $this->connectionsToTransact : [null];
}

Which is called and looped over whenever creating / rolling back transations.

Please or to participate in this conversation.