When to use `afterRefreshingDatabase`

Published 17/12/2021

In Laravel 8.76, support for a new `afterRefreshingDatabase` method in tests was added. Want to know when and where to use it? Read on!

Use the new afterRefreshingDatabase method in your tests to get around complex setup logic for your database.

A while back, I PR'd a LazilyRefreshDatabase trait to the Laravel Framework. This came from the annoyance of having to manually include RefreshDatabase in each test, unless you wanted to suffer a performance penalty. It was very well received. So well, in fact, that it almost became the default way of refreshing your test database in new Laravel projects. Almost, until it was revealed there was a catch.

For most projects, LazilyRefreshDatabase would work without issue. I had several projects that were updated and running in less than 5 minutes. But on other projects, that wasn't quite the case.

As an example, take the TestCase from the in-progress PestPHP news site: https://github.com/pestphp/news.pestphp.com/blob/main/tests/TestCase.php. We have a non-standard migration strategy here: before each test, we need to ask Laravel to also migrate Wink's migration files, then run a seeder.

This would cause a real problem for LazilyRefreshDatabase. Why? Basically, if we did it in the setUp method, we would be completely negating any benefits provided by the LazilyRefreshDatabase trait, because the database would go back to being refreshed after every test. What if instead we use a listener that fires after database migration is complete, like so?

public function setUp()
{
parent::setUp();
 
Event::listen(MigrationsEnded::class, function () {
$this->artisan('migrate', [
'--path' => 'vendor/themsaid/wink/src/Migrations',
]);
});
}

Well, this seems like a good idea, until you run it. What we've actually caused here is an endless loop, because the Wink artisan command will cause the MigrationsEnded event to fire again. Not good. Not good at all.

What we actually need is a hook. A hook that will fire after the database has refreshed. A hook that doesn't care whether you're using RefreshDatabase or LazilyRefreshDatabase, but rather just works.

That's exactly what the new afterRefreshingDatabase method does. It will fire once the database has been refreshed (as the name implies), and gives you the perfect location to drop any test setup code like this. By using this method, we keep all of the benefits of lazily refreshing our database whilst allowing for pretty much any database setup, whether that's additional migrations, seeding or something else, that is required.

public function afterRefreshingDatabase()
{
$this->artisan('migrate', [
'--path' => 'vendor/themsaid/wink/src/Migrations',
]);
}

I have to give a shoutout to Aaron Francis here, who inspired me to write this PR after this conversation on Twitter: https://twitter.com/aarondfrancis/status/1469331525422489604?s=20.

So, now you know why and how to use afterRefreshingDatabase in your Laravel tests. Since adding this method to the Laravel Framework, I've been able to adopt LazilyRefreshDatabase in all of my projects without any issues!

Kind Regards,

Luke

Like what you see?

If you enjoy reading my content, please consider sponsoring me. I don't spend it on cups of coffee; it all goes towards freeing up more of my time to work on open source, tutorials and more posts like this one.