Tony Messias
Pretty neat to see the Rails parallel test runner peg all cores and hyperthreads on my 8-core iMac. 10,000 assertions across 2,000 tests completing in 1 minute, 29 seconds. No fancy magic! All hitting the Dockerized DB. (This is for a 0.8 test ratio on an app that's 25KLOC.) pic.twitter.com/59xCf1lMp6
— DHH (@dhh) October 29, 2020
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:
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.
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.