- Authors
- Name
- Harrison Broadbent
- @hrrsnbbnt
In this article, I explain how to run your RSpec tests in parallel — and bank a
2x-4x
speedup in the process — using the parallel_rspec gemparallel_rspec is a great gem that makes it easy to run your specs in parallel — it handles cloning your database and running tests across
N
parallel workers. Further down I also benchmark the optimal number of workers and... turns out the defaults are pretty perfect 😅
Table of Contents
Overview
RSpec runs specs serially in a single CPU process. That is, one-by-one, using a single lonesome CPU core from your beefy dev machine. This is in contrast to Minitest + Rails, which natively supports parallel testing.
Given modern development machines typically have 8, 16, even 32+ powerful CPU cores, and plenty of RAM to boot, it would be great if we could actually make use of that hardware to run our specs in parallel (and hence much faster!).
We'd expect a sizeable speedup, and that's exactly what we see, if we run our RSpec specs with a gem like parallel_rspec. This gem makes it easy to run specs across N
parallel workers (default N=4
), leading to a 2x-4x
speedup (approx).
I've tried a few different parallel RSpec gems and by far prefer
parallel_rspec
. Another popular option is parallel_tests, but I've always found it fiddly and annoying to deal with. In contrast,parallel_rspec
has been rock-solid.
Speed comparison
Here's a speed comparison between vanilla rspec
and parallel_rspec
(using the default N=4
workers) running the AttendList test suite on my base M2 MacBook Air.
It contains 279 examples
across a variety of spec types:
Vanilla RSpec
❯ rspec ........................................................................ ................................................. Finished in 32.39 seconds 🐌
Parallel RSpec
❯ bin/parallel:rspec ........................................................................ ................................................. Finished in 10 seconds 🤯
Running specs in parallel leads to a ~3.2x
speedup, saving 22.39
seconds.
parallel_rspec
Getting started with Setting up the parallel_rspec
gem is very straightforward (that's a big part of why I like this gem!)
Just add it to your Gemfile:
group :development, :test do gem 'parallel_rspec' end
then install it with bundle
.
The parallel_rspec
gem runs each thread against its own database instance (to avoid deadlocks), so prepare the test databases & run your specs with:
# 1. prepare test databases bundle exec rake db:parallel:create db:parallel:prepare # 2. run specs in parallel bundle exec prspec spec/
That's it! Your specs should run substantially faster than just running rspec
.
Keep reading to explore my benchmarks and handy bin/parallel: scripts.
By default,
prspec
will use 4 workers to run your tests.You can tweak this by adjusting the
$WORKERS
environment variable. However, I've found that 4 workers is pretty perfect, so be careful when tweaking this. If you do adjustWORKERS
, ensure you re-run the:create
and:prepare
commands.
Each
prspec
thread runs against a separate instance of your database (created when runningdb:parallel:create
and migrated withdb:parallel:prepare
). It automatically handles creating and naming your additional database instances.
Fine-tuning the number of workers
I've benchmarked prspec
using 1-8
workers. The (rough and only vaguely scientific!) data I've collected is below:
tldr; the default
WORKERS
value of4
seems optimal, at least for my machine.
N workers | time |
---|---|
1 (rspec, no parallel) | 21.7 seconds |
2 | 12.4 seconds |
3 | 9.4 seconds |
4* | 7.5 seconds |
5 | 8.9 seconds |
6 | 12.2 seconds |
7 | 16.6 seconds |
8 | 17.1 seconds |
Here's that data plotted:

I've run similar benchmarks on more powerful machines and got comparable results. With a beefy enough computer, though (and/or fewer other programs running), you may find N=5
or even N=6
to be optimal.
However, for most people, N=4
will deliver speedup enough without the need to re-run these benchmarks.
Note: Keen observers will note that
rspec
ran significantly faster here than (21.7 seconds
) compared to the previous section (32.39 seconds
). I ran these benchmarks against an earlier test suite of AttendList with fewer specs. The speedup in this section is similar, though — around2.9x
bin/parallel:
scripts
Handy I've put together a couple of scripts to encapsulate the 2 key things you'll do with parallel_rspec
:
- setup the test databases — bin/parallel:prepare,
- run specs (in parallel!) — bin/parallel:rspec
These scripts are modelled after the same bin/
pattern as bin/dev
bin/parallel:prepare
A short wrapper script to create & prepare test databases for each parallel thread:
#!/usr/bin/env sh # Prepare copies of DB for parallel_rspec # https://github.com/willbryant/parallel_rspec # bundle exec rake db:parallel:create db:parallel:prepare
bin/parallel:rspec
Wraps the actual spec running aspect of parallel_rspec
. You call it like so:
bin/parallel:rspec # all specs bin/parallel:rspec spec/models # subset of specs
#!/usr/bin/env sh # Runs specs in parallel with parallel_rspec. # By default runs in 4 groups (determined by WORKERS env) # If no args are provided, run the whole suite; otherwise, run the given path(s). # # https://github.com/willbryant/parallel_rspec # set -e if [ "$#" -eq 0 ]; then exec bundle exec prspec spec/ # run all specs if no path provided else echo "Running parallel specs in $*..." # prints "running parallel specs in spec/path/to/specs" exec bundle exec prspec "$@" fi
Conclusion
I've been using the parallel_rspec gem a ton lately in my side projects, and it's worked so well that I've recently introduced it at my workplace too.
The speed gains from running specs in parallel are no-joke — ~3x
in this article — and parallel_rspec
so far has been rock-solid, without any of the troubles I had dealing with parallel_tests.