By:
Tim Chandler
Published on 2021-03-09

Adapt - A Database Preparation Tool

Adapt is a Laravel package that unifies the process of building your test-databases. It's a drop-in replacement for Laravel's database-building traits, with a focus on speed and convenience.

Because of its unified nature, it's able to combine things like parallel testing and browser testing. You can run your Dusk browser tests in parallel, and even include them in your normal test-run without needing to run php artisan dusk.

But what's better is it's easy to use. You don't need to worry about the details, it's automatic!

It's very configurable, but most likely you won't need to customise it.

Like Laravel's RefreshDatabase trait, it wraps your tests in transactions.

For a quick-start, it re-uses your test-databases from previous runs. It's careful to make sure it's safe to do so, and will re-build them automatically when needed.

It detects when you're running tests in parallel, and creates a database for each process.

During browser tests (where transactions can't be used), it can take snapshot dumps and import them instead - so your migrations and seeders don't need to run each time.

It logically separates databases into "scenarios", such as when you specify particular seeders that a test uses. Each scenario gets its own database, so they can be reused without conflicting with the others.

If your project uses multiple databases at the same time, it can build them for your tests.

It also cleans up after itself by removing test-databases and snapshot dump files when they're not valid anymore.

I have written a free online-book - Fast Test-Databases, which describes these techniques in detail.

Rationale

Adapt was built from the need to improve the speed of building a large project's database for tests, which took 40 seconds(!). Moving from Laravel Homestead on a Windows laptop, to Docker (on WSL2), and finally to Docker on a new Linux laptop, the time was reduced to 10 seconds.

Update: I've improved the speed further by running MySQL from a memory filesystem.

But this is still a long time to wait when running tests over-and-over. So during this journey of updating my dev environment, I started working on Adapt.

I started with the logic needed to safely re-use databases between test-runs. If it was left in a clean-state, this whole 10 second wait could be skipped!

From there I added new features, and fine-tuned them to work well together.

The feature I'm most excited about is the ability to share config settings between test and browser-request processes. This new idea allows for parallel browser tests!

Installation

Simply require the package into your project:

composer require code-distortion/adapt --dev

And use the LaravelAdapt trait in your test-classes instead of RefreshDatabase:

<?php
// tests/Feature/MyTest.php

namespace Tests\Feature;

use CodeDistortion\Adapt\LaravelAdapt;
//use Illuminate\Foundation\Testing\DatabaseMigrations;
//use Illuminate\Foundation\Testing\DatabaseTransactions;
//use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class MyTest extends TestCase
{
    
use LaravelAdapt;
// use DatabaseMigrations; // not needed // use DatabaseTransactions; // not needed // use RefreshDatabase; // not needed … }

Then run your tests like normal:

php artisan test
# or
php artisan test --parallel

Feature List

Here's a more detailed list of the features included in Adapt:

  • Simple installation: Use Composer to require the package, and replace the RefreshDatabase trait in your tests with LaravelAdapt. Most likely you won't need to do anything more than that,
  • Compatibility: MySQL, SQLite and SQLite memory databases, Laravel 5.1 - 9, PHP 7.0 - 8.1, PHPUnit & PEST tests, ParaTest & Dusk,
  • Tests wrapped in transactions: Speeds up each test by undoing changes quickly afterwards, leaving the database clean for the next. This can be disabled when tests use their own transactions,
  • Database re-use: Safely detects when databases can be re-used the next time you run your tests, to save that initial build-time,
  • Custom SQL dump imports: Custom SQL dump files can be imported before migrations are run,
  • Choose migrations: Each test can specify which migrations to run before running seeders (you know, just in case you happen to have more than one set of migrations),
  • Choose seeders: Specify which seeders to run before each test starts. Adapt will incorporate them into it's caching system,
  • Separate databases: Some of your tests might use different seeders (or even different migrations) to the rest. To avoid conflicts, Adapt builds a separate database for each combination, allowing them to be re-used the each time you run your tests,
  • Browser testing: Adapt is compatible with Dusk browser tests. Include them in your normal test-run, no need to run php artisan dusk,
  • Parallel tests: Run multiple tests at the same time. Databases are created for each process when using Laravel's parallel testing feature, or ParaTest directly. Dusk browser tests can be included when parallel testing,
  • Database snapshots (dumps): Snapshot dump files can be taken automatically after building the database, and imported again later when needed instead of running migrations + seeders.
  • Build multiple databases per-test: Your project might use two or more databases at the same time. Adapt lets you build each of these test-databases (with their own migrations and seeders),
  • Automatic invalidation and removal of old databases: Changes to your migration, seeder, and factory files invalidate existing test-databases. When detected, your old databases (and snapshot dump files) are removed, and your databases are rebuilt,
  • Configuration: Customise these settings via Adapt's config file, or per-test class via class properties.

And that's it really! Let me know what you think.

Previous: Fast Test-Databases