Mutant includes a parallel test runner that can run RSpec or Minitest tests without performing mutation testing. This is useful for:
- Onboarding to mutant: Verify that the test suite works correctly with mutant's configuration before introducing mutations
- Fast parallel test execution: Leverage mutant's parallel execution infrastructure for regular test runs
- CI pipelines: Run tests in parallel as a prerequisite before mutation testing
- Debugging: Isolate test failures from mutation-related issues
Before running mutation testing with mutant, it is recommended to first verify the test suite runs correctly using the test runner. This helps ensure:
- The mutant configuration is correct
- Tests pass in mutant's execution environment
- Parallel execution works properly with the application
- Database isolation or other resource management is configured correctly
Runs all tests in parallel using mutant's test runner.
bundle exec mutant testThis command:
- Uses the same configuration as
mutant run(from.mutant.yml,config/mutant.yml, ormutant.yml) - Runs tests in parallel across multiple worker processes
- Respects the
jobsconfiguration orMUTANT_JOBSenvironment variable - Uses the configured integration (RSpec or Minitest)
- Applies any hooks configured in the hooks files
Exit Status:
- Returns
0if all tests pass - Returns non-zero if any tests fail
Lists all tests detected in the environment without running them.
bundle exec mutant environment test listThis command:
- Loads the mutant environment and integration
- Discovers all available tests
- Prints the test count and identification of each test
- Useful for verifying test discovery is working correctly
The test runner uses the same configuration file as mutation testing. All relevant settings apply:
# .mutant.yml
integration:
name: rspec # or minitest
requires:
- my_app
includes:
- lib# .mutant.yml
jobs: 8 # Number of parallel workers
environment_variables:
RAILS_ENV: test
hooks:
- rails_hooks.rb # For database isolation, etc.The number of parallel jobs can be configured through:
- CLI option:
-jor--jobs - Environment variable:
MUTANT_JOBS - Configuration file:
jobs: - Default: number of processors on the system
For example:
MUTANT_JOBS=4 bundle exec mutant testPass additional arguments to the test framework using integration arguments:
Via Configuration File:
integration:
name: rspec
arguments:
- --fail-fast
- --seed
- '0'
- specVia Command Line:
bundle exec mutant test --integration-argument --seed --integration-argument 0Note: Each argument must be specified separately with its own --integration-argument flag.
When running tests in parallel, proper isolation of shared resources is critical. The most common requirement is database isolation.
For Rails applications, use hooks to create isolated database resources for each worker:
# .mutant.yml
hooks:
- rails_hooks.rbSee Rails Integration for the full setup, including PostgreSQL and SQLite per-worker database isolation examples. The Hooks documentation describes the hook events used.
Consider isolation requirements for:
- File system operations (temp files, uploads, etc.)
- External services (use test doubles or worker-specific endpoints)
- Caches (Redis, Memcached with worker-specific namespaces)
- Environment variables (set per-worker if needed)
First, ensure the test runner works correctly:
# Run tests in parallel to verify configuration
bundle exec mutant test
# If failures occur, debug with fewer workers
bundle exec mutant test -j 1
# List tests to verify discovery
bundle exec mutant environment test listOnce tests pass reliably with the test runner, introduce mutation testing:
# Start with a small scope
bundle exec mutant run --fail-fast YourClass#some_method
# Expand to broader scope
bundle exec mutant run YourNamespace*Use the test runner in CI before running mutation testing:
# .github/workflows/ci.yml or similar
- name: Run tests in parallel
run: bundle exec mutant test
- name: Run mutation testing (incremental)
run: bundle exec mutant run --fail-fast --since HEAD~1Compared to rspec --parallel or similar tools:
Advantages:
- Uses mutant's robust process isolation
- Consistent environment with mutation testing
- Leverages existing mutant configuration and hooks
- Battle-tested parallel execution infrastructure
- Dynamic work allocation: Workers pull tests from a shared queue on-demand, providing better load balancing for slow tests
Differences:
- Does not support all RSpec CLI options directly (use
--integration-argument) - Requires mutant configuration to be set up
- Different output format
Work Allocation:
Mutant uses dynamic work allocation where workers pull tests from a shared queue as they become available. This differs from static pre-allocation used by most parallel test runners.
- Static allocation (typical RSpec parallel runners): Work is divided upfront before execution starts, based on file count, file size, or historical runtime data
- Dynamic allocation (mutant): Workers pull the next test when ready, distributing work at runtime based on actual performance
Trade-offs:
- More effective for slow tests: Dynamic allocation prevents workers from sitting idle while others process slow tests. Particularly beneficial for Rails integration tests with variable execution times.
- Less effective for many fast tests: Dynamic allocation has coordination overhead. For suites with thousands of very fast unit tests (< 10ms each), static allocation may be more efficient. Note: Mutant intends to address this limitation in the future via dynamic batching.
- Better load balancing: Fast workers automatically get more work, slow workers don't hold up the entire suite
Compared to rake test or minitest parallel runners:
Advantages:
- Automatic parallel execution without additional gems
- Process isolation prevents test pollution
- Consistent with mutation testing environment
- Dynamic work allocation (same benefits as described for RSpec above)
Differences:
- Different CLI interface
- Requires mutant configuration
This indicates shared resource conflicts. Common causes:
- Database conflicts: Implement database isolation with hooks
- File system conflicts: Use unique temp directories per worker
- Global state: Avoid or properly isolate global variables/constants
- External services: Mock or use worker-specific instances
Debug by running with a single worker:
bundle exec mutant test run -j 1If mutant environment test list shows no tests or fewer than expected:
- Verify integration configuration (
integration.nameis set correctly) - Check
requiresloads the test framework and application - Ensure
includespoints to the correct directories - For RSpec: verify
specdirectory is in integration arguments
If tests run slower than expected:
- Check job count is appropriate for the system:
mutant test run -j 4 - Verify hooks aren't performing expensive operations
- Consider if database setup is duplicating work across workers
- Profile individual tests to find bottlenecks
If mutant cannot load the environment:
- Verify configuration file exists and is valid YAML
- Check
requirespaths are correct - Ensure
environment_variablesare set correctly (e.g.,RAILS_ENV: test) - Review
includespaths
To run specific tests, use integration arguments:
RSpec:
bundle exec mutant test run --integration-argument spec/modelsMinitest:
bundle exec mutant test run --integration-argument test/modelsOverride job count for specific runs:
# Use all available processors
bundle exec mutant test run
# Limit to 4 workers
bundle exec mutant test run -j 4
# Single worker for debugging
bundle exec mutant test run -j 1The test runner can complement existing test infrastructure:
# Run fast tests with native runner
bundle exec rspec spec/unit
# Run slow integration tests with mutant's parallel runner
bundle exec mutant test run --integration-argument spec/integration- Configuration - Complete configuration options
- Hooks - Database isolation and custom behavior
- Concurrency - Understanding parallel execution
- Rspec Integration - RSpec-specific details
- Minitest Integration - Minitest-specific details
- Test::Unit Integration - Test::Unit-specific details