Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Fast Refresh Database Laravel Package

plannr/laravel-fast-refresh-database

Speed up database refreshes in your Laravel test suite. This package replaces slow migrate:fresh runs with a faster refresh strategy, so each test starts from a clean database without the long wait. Drop-in, configurable, and built for quick feedback loops.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require plannr/laravel-fast-refresh-database --dev

The package is a drop-in replacement for Laravel's RefreshDatabase trait. Instead of running migrate:fresh on every test process, it hashes your migration files, and if nothing changed since the last run it skips the migration step entirely and just wraps each test in a transaction against the already-migrated database. That means the first run costs what RefreshDatabase normally costs, and every subsequent run in a tight TDD loop is close to free.

Swap the trait in your base test case:

// tests/TestCase.php
namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Plannr\LaravelFastRefreshDatabase\FastRefreshDatabase;

abstract class TestCase extends BaseTestCase
{
    use FastRefreshDatabase;
}

For Pest, do it in tests/Pest.php instead:

uses(Plannr\LaravelFastRefreshDatabase\FastRefreshDatabase::class)->in('Feature');

Optionally publish the config (php artisan vendor:publish --tag="fast-refresh-database-config" — confirm the exact tag in the README, the package is small and the tag name is the kind of thing that drifts between versions).

Where to look first:

  1. vendor/plannr/laravel-fast-refresh-database/composer.json — check the illuminate/* constraints against your Laravel version before you invest in adopting it. The last tagged release is April 2024, so Laravel 12 support is not guaranteed.
  2. vendor/plannr/laravel-fast-refresh-database/src/ — the trait is short. Read it end to end; you need to know exactly what it hashes and where it stores that hash, because that determines every gotcha below.
  3. Your slowest test file — run it once, then again, and compare wall time.

First use case: you have a feature-test suite where the initial migrate:fresh is a meaningful chunk of the run (say 8–20s on MySQL/Postgres), and you re-run individual test files dozens of times an hour. That is the sweet spot. If your suite already runs on SQLite :memory:, there is little to gain — in-memory databases can't persist a migrated schema between processes, so the hash will always miss.

Implementation Patterns

Put it on the base class, not per-test. The value comes from every test sharing the same "schema is current" decision. Per-test trait selection fragments that and makes behaviour hard to reason about.

Choose the right trait per test type. FastRefreshDatabase is for tests that need real migrations. Tests that only need a transaction around pre-existing tables are cheaper with DatabaseTransactions. Mixing them is fine and normal:

// tests/Feature/CheckoutTest.php — needs full schema
use FastRefreshDatabase;

// tests/Feature/ReportQueryTest.php — schema already guaranteed by the suite
use Illuminate\Foundation\Testing\DatabaseTransactions;

Keep seeders and factories idempotent. Because the migrated database survives between runs, anything you seed in setUp accumulates unless it's wrapped in the transaction or uses firstOrCreate/updateOrCreate. Prefer factories inside the test over seeding in setUp.

TDD loop pattern. This is where it earns its keep:

# run 1 — pays the migrate cost
php artisan test --filter=CheckoutTest
# run 2..n — near-instant
php artisan test --filter=CheckoutTest

Deliberately break the loop by touching a migration, and you'll see the refresh kick back in. That feedback is useful: it tells you the hash inputs include the file you just edited.

CI: make the cache survive. On a clean runner you get the slow path unless you persist the package's state between jobs. If the state lives in a file, cache it:

- uses: actions/cache@v4
  with:
    path: <whatever path the trait writes its hash to — check src/>
    key: fast-refresh-${{ hashFiles('database/migrations/**') }}

Keying on hashFiles('database/migrations/**') mirrors what the package does internally, so the cache invalidates exactly when a real refresh is needed.

Parallel testing needs per-worker databases. The trait's "schema is current" bookkeeping is per-database. With php artisan test --parallel, give each worker its own database (ParallelTesting::setUpTestDatabase or a DB_DATABASE suffix) so one worker's refresh doesn't invalidate another's assumptions.

Compose with a schema dump for cold starts. If you also use php artisan schema:dump, the first-run cost drops and the trait's skip path handles the rest. They solve adjacent problems and coexist fine.

Gotchas and Tips

Editing an existing migration is the classic trap. If the hash covers file contents, you're safe; if it only covers filenames (or timestamps), a changed migration will be silently ignored and you'll debug against a stale schema for twenty minutes. Verify this explicitly on day one: edit a column in an existing migration, run the suite, and confirm the schema actually changed. If it didn't, add a "force refresh" step to your workflow.

Know your escape hatch. Whatever the package's mechanism — a config flag, an env var, or deleting a state file — find it and document it in your README. When schema and code disagree at 5pm on a Friday, rm on the state file plus a full run is the fix.

Transactions break on implicit commits. MySQL DDL (CREATE TABLE, ALTER TABLE) causes an implicit commit, which silently ends the wrapping transaction and leaks state into the next test. If a test runs DDL, it needs to be isolated differently. Postgres DDL is transactional and doesn't have this problem — one more reason Postgres is the friendlier CI target here.

Don't let it near a real database. The trait assumes it owns the schema. Add a guard in your base test case:

protected function setUp(): void
{
    if (! app()->environment('testing')) {
        throw new \RuntimeException('FastRefreshDatabase must only run in testing.');
    }
    parent::setUp();
}

Version compatibility is the biggest adoption risk. Last release April 2024, zero dependents on record. Laravel 11 and 12 reworked parts of the testing/refresh pipeline (migrateUsing(), parallel-testing hooks, RefreshDatabase internals). Pin the package, run your full suite on a branch before merging, and keep a one-line revert to RefreshDatabase ready. Don't adopt it in the same PR as anything else.

Multiple connections need explicit handling. If you assert against a second connection, set $connectionsToTransact — the same rule as RefreshDatabase, but worth restating because a skipped migration makes a wrong-connection read look like a passing test.

Debugging aid: when a test behaves as if a table or column is missing, run php artisan migrate:status against your test database and cross-check the actual schema, rather than trusting the trait's decision. Adding a temporary Schema::hasColumn(...) assertion at the top of the failing test isolates "stale schema" from "bad test" in seconds.

Extension point: the trait is a plain PHP trait — subclass your base TestCase, override the refresh method, and log when a real refresh happens. One line of logging turns this from a black box into something you can reason about during CI investigations.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package