No release in over 3 years
A RuboCop plugin that provides custom cops to ensure RSpec test coverage parity and enforce RSpec best practices in your Ruby projects.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 1.72.0
 Project Readme

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.rb

RSpecParity/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
end

RSpecParity/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
end

Configuration options:

  • IgnoreMemoization (default: true) - When enabled, common memoization patterns like @var ||= and return @var if defined?(@var) are not counted as branches. Set to false if 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.rb should have a spec at spec/models/user_spec.rb.
  • Spec file naming: Spec files use the _spec.rb suffix.
  • 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 SufficientContexts cop counts context blocks within a method's describe block 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: false

And then execute:

bundle install

Or install it directly:

gem install rubocop-rspec_parity

Usage

Add rubocop-rspec_parity to your .rubocop.yml:

require:
  - rubocop-rspec_parity

# For RuboCop >= 1.72
plugins:
  - rubocop-rspec_parity

The 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 rubocop

Development

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.