Crspec
Crspec is a next-generation concurrent, fiber-isolated Ruby testing framework ecosystem. By leveraging Ruby 3.2+ Fiber Storage (Fiber[:key]), fiber-aware method interceptors, non-blocking fiber schedulers, and ruby/prism AST transpilation, Crspec achieves ultra-fast concurrent test execution while maintaining full syntax compatibility with traditional RSpec suites.
Key Features
- Fiber Storage Context Isolation: Solves legacy thread-local (
Thread.current) state leakage by routing all execution metadata andletmemoization through inherited Fiber storage (Fiber[:crspec_execution_context]). - Fiber-Aware Mocking (
crspec-mock): Prevents mock registry corruption across parallel tests using prepended method interceptors bound toFiber[:crspec_mock_space]. - Rails Connection Leasing & Parallel Testing (
crspec-rails): Leases database connections per example fiber (ActiveRecord::Base.lease_connection), handles savepoint transaction strategies (SAVEPOINT ex_root), and integrates with Rails parallel worker suites (Crspec::Rails::Parallel). - Prism-Based AST Transpiler (
crspec-transpiler): Automated migration tool usingruby/prismC-parser to convert existing RSpec codebases to Crspec syntax and flag thread-unsafe constructs likebefore(:all). - Concurrent Execution Kernel: Multi-threaded worker pool integrated with non-blocking fiber schedulers (
Async::Scheduler).
Installation
Add crspec to your application's Gemfile:
gem "crspec", github: "crspec/crspec"
Then install via Bundler (or mise):
mise exec -- bundle install
Writing Specs
crspec provides a familiar RSpec-style DSL for structuring example groups and expectations:
require "crspec"
Crspec.describe User, type: :model do
let(:valid_attributes) { { name: "Jane Doe", email: "jane@example.com" } }
subject(:user) { User.new(valid_attributes) }
it "validates primary attributes" do
expect(user.valid?).to be(true)
end
context "when email is omitted" do
let(:valid_attributes) { { name: "Jane Doe", email: nil } }
it "flags validation errors" do
expect(user.valid?).to be(false)
expect(user.errors[:email]).to include("can't be blank")
end
end
end
Supported Matchers
eq(expected)be(expected)/be_nil/be_truthy/be_falsyinclude(*items)raise_error(ExceptionClass, "message")respond_to(*methods)
Advanced: Execution Context & Extensions
Regular spec authors do not need to interact with execution_context at all—crspec automatically isolates let memoization, database connections, and stubs invisibly per test.
For gem developers, middleware, or custom test extension authors needing request tracing across child fibers, execution_context (or current_context) provides an encapsulated, thread-safe store:
before do
execution_context[:trace_id] = "TX-12345"
end
it "reads execution context metadata" do
expect(execution_context[:trace_id]).to eq("TX-12345")
end
Fiber-Isolated Mocking (crspec-mock)
crspec-mock routes all doubles and stubs through Fiber[:crspec_mock_space], allowing parallel tests to stub methods on shared target objects without cross-test pollution:
Crspec.describe PaymentGateway do
let(:stripe_client) { instance_double(Stripe::Charge) }
it "stubs credit card processing concurrently" do
allow(stripe_client).to receive(:create).with(amount: 5000).and_return(status: "succeeded")
result = stripe_client.create(amount: 5000)
expect(result[:status]).to eq("succeeded")
end
end
Rails Concurrency & Parallel Testing (crspec-rails)
Database Isolation & Connection Leasing
Wrap examples in connection leasing and transaction savepoints:
Crspec.describe User, type: :model do
around do |example|
Crspec::Rails::DatabaseIsolation.wrap_example(example)
end
end
Rails Parallel Testing Setup
Configure worker counts and setup/teardown hooks:
Crspec::Rails::Parallel.parallelize(workers: 4) do
parallelize_setup do |worker_number|
# Prepare worker database, seed data, etc.
puts "Worker #{worker_number} starting (TEST_ENV_NUMBER=#{ENV['TEST_ENV_NUMBER']})"
end
parallelize_teardown do |worker_number|
puts "Worker #{worker_number} tearing down"
end
end
Multi-Fiber Integration Server
Boot an embedded multi-threaded/multi-fiber Puma server for system/browser specs:
Crspec::Rails::SystemServer.start_concurrent_server!(Rails.application, 9887)
Migrating from RSpec (crspec-transpile)
Use the built-in Prism-powered transpiler CLI to convert legacy RSpec suites to Crspec:
# 1. Analyze your test directory for thread-unsafe patterns (e.g. before(:all))
mise exec -- crspec-transpile --analyze spec/
# 2. Automatically transpile RSpec code to Crspec syntax in-place
mise exec -- crspec-transpile --write spec/
Running Specs
Execute your test suite concurrently using the crspec executable:
# Run specs with default concurrency (number of CPU cores)
mise exec -- crspec spec/
# Run specs with specific concurrency
mise exec -- crspec -c 8 spec/models/
Development & Testing
To run the internal framework unit tests (built with Minitest):
mise exec -- bundle exec rake test
License
The gem is available as open source under the terms of the MIT License.