ᐊ back to home

Running Laravel Feature Tests in Parallel with Paratest

Tony Messias

Ever since I saw that Rails 6 was shipping with a parallel test runner I got curious if we couldn't have something like this for Laravel and PHPUnit.

I knew there was Paratest, which allows running your PHPUnit tests in parallel without much trouble. By default, it separates test classes in groups and run each group in a different process.

That was awesome, but I faced an issue. My feature tests hit the database and, since each process will try to migrate the database, instead of getting a speed boost, I got a lot of errors.

So I started tinkering with a package to make this experience easier. After exploring Rails itself, I noticed that each process creates its own database, which makes sense.

At this point I had two options:

  1. I could swap the RefreshDatabase trait for the DatabaseTransaction one and manage the test database migration myself (probably would be the easiest route); or
  2. I coul find a way to programatically create one database for each test process.

I decided to follow the second route, because I'd like to avoid having to remember to run the migrations before running the tests every time I pulled some changes. This turned out to be possible. Paratest, by default, creates an environment variable called TEST_TOKEN and each process gets assigned a unique one (unique for the test run).

So I implemented some artisan commands, such as the db:create one, and also a custom test runner that would create the database before the process runs the test. Essentially, this ends up mimicking the same behavior from Rails: each process creates its own database, which is migrated once per process and each test runs in a transaction, which is rolled back after each test.

Here's the project on GitHub, I've recently upgraded it to Laravel 8. It's already available on Packagist, so you can already pull it locally and try to use it yourself.

Laravel and Paratest

To be honest, I don't need such feature because my tests tend to run quite fast. But something like this might be handy on bigger projects or in a CI environment.