How to Parallelize Your RSpec Test Suite Locally
Waiting over an hour for a test suite to finish is a productivity killer.
On a recent project, that was our reality.
Running the full local RSpec suite took almost 2 hours, making it difficult to get quick feedback and confidently iterate on changes. While there are many ways to optimize test performance including fixtures, request stubbing, faster tooling, and more, most of these options require significant effort to implement.
Instead, we explored a simpler approach: bringing parallel test execution to local development. The result was a much faster feedback loop (down to 5 minutes) and a significantly better developer experience.
Although our primary goal was to improve the local developer experience, this setup can be used in both local and CI environments.
In this post, we’ll walk through how to set up parallel testing locally in Rails, from configuring worker-specific databases to preparing your application for concurrent test execution.
Setting up parallel tests
Since this project uses RSpec, we evaluated two popular gems: parallel_tests and turbo_tests . We ultimately chose turbo_tests, which builds on top of parallel_tests while providing cleaner, incremental test output. This summarized output makes it easier to monitor progress and improves the overall developer experience when running large test suites.
After installing the gem, running tests in parallel is as simple as:
bundle exec turbo_tests
Under the hood, turbo_tests splits the test suite across multiple processes. Each process receives a unique value through the TEST_ENV_NUMBER environment variable, allowing resources such as databases to be isolated between workers:
ENV["TEST_ENV_NUMBER"]
In your database.yml file, configuring the test database as:
test:
database: myapp_test<%= ENV['TEST_ENV_NUMBER'] %>
allows each parallel test worker to connect to its own database.
For example, the configuration above will generate databases such as:
myapp_test1
myapp_test2
myapp_test3
…
When turbo_tests launches multiple workers, each process is assigned a unique TEST_ENV_NUMBER and connects to its corresponding database. This isolation prevents workers from interfering with one another during test execution.
Isolating Redis and Cache Resources
Databases aren’t the only resources that may need to be isolated when running tests in parallel. Applications that use Redis for caching, background jobs, or other shared state should also ensure that parallel workers don’t interfere with one another.
One approach is to use the worker’s TEST_ENV_NUMBER to create a unique namespace for each process:
namespace = "test#{ENV['TEST_ENV_NUMBER']}"
For example, Rails cache can be configured with a worker-specific namespace:
config.cache_store = :redis_cache_store, {
namespace: "cache:test#{ENV['TEST_ENV_NUMBER']}"
}
This ensures that cache entries created by one worker are isolated from the others, even when all workers share the same Redis instance.
Redis Databases
In some cases, adjusting the namespace may not be enough to isolate Redis between the different workers there’s code ignoring the namespace. When that happens, an alternative is the use of different Redis databases by changing the URL:
# before
REDIS_URL = "redis://localhost:6379/0"
# after
redis_database = ENV.fetch('TEST_ENV_NUMBER', '1').to_i - 1
REDIS_URL = "redis://localhost:6379/#{redis_database}"
Setting up the databases
To ensure parallel tests run successfully, each worker’s database must be fully created, migrated, and prepared before the test suite starts.
Because turbo_tests is built on top of parallel_tests, it already includes tasks for preparing worker databases. For applications that follow Rails’ standard database setup conventions, the built-in tasks may be all that’s needed.
For example, the following command will create and prepare the test databases used by each parallel worker:
bundle exec rake parallel:prepare
Depending on your Rails version and setup, you may also use:
bundle exec rake parallel:create
bundle exec rake parallel:migrate
In our case, the test database required additional setup steps, so we created a custom script to prepare a dedicated database for each worker:
# at bin/setup_parallel_databases
# Determine the number of CPUs on the environment being used
CPU_COUNT=$(getconf _NPROCESSORS_ONLN)
# Set up the database for every process
for databaseName in $(seq 1 $CPU_COUNT); do
TEST_ENV_NUMBER=$databaseName RAILS_ENV=test bundle exec rake \
db:drop \
db:create \
db:environment:set \
db:schema:load \
...
done
The exact database setup steps will vary depending on your application’s requirements, but the general approach remains the same.
The script determines how many processors are available on the local machine and creates a dedicated test database for each worker by assigning a unique TEST_ENV_NUMBER.
This ensures that every worker starts with an isolated, fully initialized database containing:
- The latest schema
- Rails environment metadata
- Seed data (if required)
By provisioning a dedicated database for each worker ahead of time, tests can run in parallel without competing for shared database resources.
Conclusion
After enabling parallelization, our test suite runtime dropped from almost 2 hours to under 5 minutes. More importantly, developers were able to get feedback locally instead of relying on the CI pipeline, making it much easier to iterate on changes and validate work before pushing code. Another benefit mentioned by the client was that they were staring to add AI agents to this application, and with this speed improvement it was now possible to include the full test suite execution as part of the instructions for the agent.
While results will vary depending on the size of your test suite and the resources available on your machine, parallelization can provide significant improvements with relatively little application-level code change.
Local test execution doesn’t have to be a bottleneck. With a relatively small amount of setup, parallelization can dramatically reduce wait times and help developers iterate with confidence.
Do you need help with your slow tests? We can take a look!