Multiple paths exist for configuring RSpec. For example, when using bundler gem demo to build a demo gem, you’ll end up with the following configuration:
RSpec.configure do |config|
config.example_status_persistence_file_path = ".rspec_status"
config.disable_monkey_patching!
config.expect_with :rspec do |c|
c.syntax = :expect
end
end
This isn’t ideal due to being extremely bare bones. Another example is when using Hanami to build a demo application via hanami new demo:
RSpec.configure do |config|
config.disable_monkey_patching!
config.shared_context_metadata_behavior = :apply_to_host_groups
config.filter_run_when_matching :focus
config.example_status_persistence_file_path = "spec/examples.txt"
config.profile_examples = 10
config.order = :random
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
if config.files_to_run.one?
config.default_formatter = "doc"
end
Kernel.srand config.seed
end
The above gets us closer to a more robust configuration but is still missing a few conveniences and checks. Here’s what’s produced when using Rubysmith:
RSpec.configure do |config|
config.color = true
config.disable_monkey_patching!
config.example_status_persistence_file_path = "./tmp/rspec-examples.txt"
config.filter_run_when_matching :focus
config.formatter = ENV.fetch("CI", false) == "true" ? :progress : :documentation
config.order = :random
config.shared_context_metadata_behavior = :apply_to_host_groups
config.warnings = true
config.expect_with :rspec do |expectations|
expectations.syntax = :expect
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_doubled_constant_names = true
mocks.verify_partial_doubles = true
end
Kernel.srand config.seed
end
With the above in mind, we can break down what each configuration is doing and why it’s important.
Breakdown
Some of the above configuration might not make sense or be initially intuitive without studying the docs so here’s a line-by-line breakdown.
config.color = true
Ensures output is colorized for improved readability.
config.disable_monkey_patching!
By default, RSpec will monkey patch your environment which leads to confusion and lack of debugability so keep this disabled so you can use RSpec.describe instead of describe when defining your specs.
config.example_status_persistence_file_path = "./tmp/rspec-examples.txt"
Ensures spec status is written to file so you can quickly fix or refactor broken code. This is a major boon to productivity since having this file allows you to run commands like rspec --only-failures or rspec --next-failure.
config.filter_run_when_matching :focus
Ensures you can add focus: true or :focus (shorthand), or fdescribe/fit (even shorter shorthand) when running only focused tests.
config.formatter = ENV.fetch("CI", false) == "true" ? :progress : :documentation
Ensures RSpec formats output in compressed/dotted progress when running on CI because you generally don’t need verbose output in that environment. On the flip side, you’ll get full documentation when running locally so you can debug any spec failure with enough context to help you quickly resolve the issue.
config.order = :random
Ensures all specs are run in random order to ensure no spec depends upon another. Catching this early and often reduces any surprises when changes are deployed in production.
config.shared_context_metadata_behavior = :apply_to_host_groups
Ensures the host group and examples inherit metadata from the shared context. This will be the default in RSpec 4.0.0.
config.warnings = true
Ensures all Ruby warnings are displayed. This can be quite verbose for some folks but is incredible powerful in finding and detecting issues early and often. You’ll also find that some upstream gem dependencies, sadly, do not adhere to this level of rigor. If that’s the case, I’d recommend installing the Warning gem to filter out and silence bad actors or remove those dependencies entirely.
config.expect_with :rspec do |expectations|
expectations.syntax = :expect
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
The first line ensures you always use expect instead of the should syntax for consistent expectations. The second line ensures custom matcher descriptions — and failure messages — include clauses from defined methods which use chain. Use of chain is not recommended but if it is used, at least you’ll get more descriptive diagnostic information.
config.mock_with :rspec do |mocks|
mocks.verify_doubled_constant_names = true
mocks.verify_partial_doubles = true
end
Ensures that doubles — both constants and objects — are properly verified, exist, and haven’t changed which would cause faulty specs, if otherwise. You definitely want both of these enabled so RSpec has your back when using RSpec Test Doubles.
Kernel.srand config.seed
Conclusion
You’ve learned there are multiple ways to configure RSpec and how you can tweak for your own preferences. You’ve also learned what each setting is and why each setting is important. By ensuring you have a solid configuration in place, you can let RSpec work for you and not the other way around.