RuboCop RSpecParity
A RuboCop plugin that enforces spec parity and best practices in RSpec test suites. This gem helps ensure your Ruby code has proper test coverage and follows RSpec conventions.
Features
This plugin provides these custom cops:
- RSpecParity/FileHasSpec: Ensures every Ruby file in your app directory has a corresponding spec file
- RSpecParity/PublicMethodHasSpec: Ensures every public method has spec test coverage
- RSpecParity/SufficientContexts: Ensures specs have at least as many contexts as the method has branches (if/elsif/else, case/when, &&, ||, ternary operators)
Examples
RSpecParity/FileHasSpec
Ensures every Ruby file in your app directory has a corresponding spec file.
# bad - app/models/user.rb exists but spec/models/user_spec.rb doesn't
# This will trigger an offense
# good - both files exist:
# app/models/user.rb
# spec/models/user_spec.rbRSpecParity/PublicMethodHasSpec
Ensures every public method has spec test coverage.
# bad - app/services/user_creator.rb
class UserCreator
def create(params) # No spec coverage for this method
User.create(params)
end
end
# good - app/services/user_creator.rb with spec/services/user_creator_spec.rb
class UserCreator
def create(params)
User.create(params)
end
end
# spec/services/user_creator_spec.rb
RSpec.describe UserCreator do
describe '#create' do
it 'creates a user' do
# test implementation
end
end
endRSpecParity/SufficientContexts
Ensures specs have at least as many contexts as the method has branches.
# bad - app/services/user_creator.rb
def create_user(params)
if params[:admin]
create_admin(params)
elsif params[:moderator]
create_moderator(params)
else
create_regular_user(params)
end
end
# spec/services/user_creator_spec.rb - only 1 context for 3 branches
RSpec.describe UserCreator do
describe '#create_user' do
context 'when creating users' do
# Only one context for 3 branches - triggers offense
end
end
end
# good - 3 contexts for 3 branches
RSpec.describe UserCreator do
describe '#create_user' do
context 'when admin' do
# tests admin branch
end
context 'when moderator' do
# tests moderator branch
end
context 'when regular user' do
# tests regular user branch
end
end
end
# Memoization patterns are ignored by default
def cached_value
@cached_value ||= expensive_operation # Not counted as a branch
end
def cached_value
return @cached_value if defined?(@cached_value) # Not counted as a branch
@cached_value = expensive_operation
endConfiguration options:
-
IgnoreMemoization(default:true) - When enabled, common memoization patterns like@var ||=andreturn @var if defined?(@var)are not counted as branches. Set tofalseif you want to count these as branches.
Assumptions
These cops work based on the following conventions:
-
File organization: Each Ruby file has a corresponding spec file stored in the
spec/directory, mirroring the same directory structure. For example,app/models/user.rbshould have a spec atspec/models/user_spec.rb. -
Spec file naming: Spec files use the
_spec.rbsuffix. -
Instance method specs: Instance methods are tested using the convention
describe '#method_name' do. -
Class method specs: Class methods are tested using the convention
describe '.class_method' do. -
Context blocks for branches: The
SufficientContextscop countscontextblocks within a method'sdescribeblock to ensure each branch path is tested.
If your project uses different conventions, these cops may not work as expected.
Installation
Add this line to your application's Gemfile:
gem 'rubocop-rspec_parity', require: falseAnd then execute:
bundle installOr install it directly:
gem install rubocop-rspec_parityUsage
Add rubocop-rspec_parity to your .rubocop.yml:
require:
- rubocop-rspec_parity
# For RuboCop >= 1.72
plugins:
- rubocop-rspec_parityThe default configuration enables all cops. You can customize them in your .rubocop.yml:
RSpecParity/FileHasSpec:
Enabled: true
Include:
- 'app/**/*.rb'
Exclude:
- 'app/assets/**/*'
- 'app/views/**/*'
RSpecParity/PublicMethodHasSpec:
Enabled: true
Include:
- 'app/**/*.rb'
RSpecParity/SufficientContexts:
Enabled: true
IgnoreMemoization: true # Set to false to count memoization patterns as branches
Include:
- 'app/**/*.rb'
Exclude:
- 'app/assets/**/*'
- 'app/views/**/*'Run RuboCop as usual:
bundle exec rubocopDevelopment
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/povilasjurcys/rubocop-rspec_parity. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the RuboCop RSpecParity project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.