0.0
The project is in a healthy, maintained state
Single-boot, multi-process execution for RSpec on POSIX systems. The parent loads RSpec once, then runs top-level groups in persistent forked workers.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 3.13
 Project Readme

RSpec::Multicore

RSpec::Multicore runs top-level RSpec example groups in persistent forked workers. The application and specs load once in the parent; workers request groups dynamically; the parent replays their standard RSpec events in seeded group order.

This repository contains:

  • rspec-multicore, the RSpec 3.13 process runner.
  • rspec-multicore-rails, the optional ActiveRecord database adapter for Rails 7.1–8.x.

Both gems require Ruby 3.2+ and a platform with fork and UNIXSocket.pair.

Installation

group :test do
  gem "rspec-multicore", "0.2.0.pre2"

  # Rails applications that use ActiveRecord:
  gem "rspec-multicore-rails", "0.2.0.pre2"
end

Require the core from your test setup. Rails applications can require the adapter instead; it loads the core automatically.

require "rspec/multicore"
# or
require "rspec/multicore/rails"

Worker count

RSPEC_MULTICORE is the only user-facing environment setting:

# Configured count or detected CPU count
bundle exec rspec
RSPEC_MULTICORE=auto bundle exec rspec

# Explicit count
RSPEC_MULTICORE=8 bundle exec rspec

# Native serial RSpec
RSPEC_MULTICORE=0 bundle exec rspec
RSPEC_MULTICORE=false bundle exec rspec
RSPEC_MULTICORE=off bundle exec rspec

Unset, true, on, and auto enable automatic selection. A positive integer selects that many workers. Unknown and negative values raise ArgumentError.

Ruby configuration is used when the environment does not provide an explicit count:

RSpec::Multicore.configure do |config|
  config.workers = 8
end

RSPEC_MULTICORE_WORKER is set to the worker's stable, one-based slot.

Process lifecycle hooks

Core has no application-specific resource handling. Use hooks for Redis, caches, profilers, coverage tools, or other process-owned state:

RSpec::Multicore.on_worker_fork do |slot|
  Redis.current.close
  SomeProfiler.output_file = "tmp/profile-#{slot}.json"
end

RSpec::Multicore.on_worker_shutdown do |slot|
  SomeProfiler.finish(slot)
end

Fork hooks run in registration order. Shutdown hooks run in reverse order; all shutdown hooks are attempted, and any failure makes the suite unsuccessful. Workers finish with exit!, so inherited at_exit callbacks do not run. Finalize worker-owned output in a shutdown hook.

Rails and ActiveRecord

The Rails adapter automatically handles only ActiveRecord:

  • It refuses to manage databases outside Rails.env.test?.
  • Worker 1 uses the base test database.
  • Later workers use _2, _3, and so on. SQLite suffixes are inserted before the extension.
  • Inherited connections are cleared before each worker establishes its own connection.

Prepare disposable databases before a parallel run:

bundle exec rake db:test:multicore:prepare
bundle exec rake db:test:multicore:drop
bundle exec rake db:test:multicore:recreate

Redis, Sidekiq, cache stores, loggers, SimpleCov, and profilers are deliberately not automatic. Configure them with the lifecycle hooks when the project needs them.

RSpec boundary

The integration patches only RSpec::Core::Runner#run_specs and delegates the suite lifecycle, hooks, summary, reporter start/stop, and exit-code calculation back to RSpec.

Parallel execution is skipped for disabled mode, unavailable fork, fewer than two groups or workers, fail-fast, and dry-run. These paths use native serial RSpec.

Workers never send RSpec or application objects to the parent. The parent keeps its original group and example objects in an ID registry. Workers send stable IDs and small execution-result or exception snapshots. The parent updates the original example's runtime description and execution result, then calls the real reporter with the original object.

Only these RSpec 3.13 reporter events are supported:

  • example_group_started, example_group_finished
  • example_started, example_finished
  • example_passed, example_failed, example_pending
  • message, deprecation, notify_non_example_exception

An unsupported reporter call fails explicitly; it does not expand the serializer. Custom formatters remain compatible when they consume RSpec's standard notifications. Events are buffered per top-level group and replayed in seeded order.

Coverage

Serial coverage is the safe default:

RSPEC_MULTICORE=0 bundle exec rspec

RSpec::Multicore does not promise automatic SimpleCov merging. Parallel coverage requires project-owned worker names, result files, shutdown finalization, and merging configured through the lifecycle hooks.

Development

Run each gem independently:

cd rspec-multicore
bundle exec rake spec
bundle exec rubocop --cache false

cd ../rspec-multicore-rails
bundle exec rake spec
bundle exec rubocop --cache false

See the development guide, contribution guide, and changelog. The project is licensed under the MIT License.