diff --git a/CHANGELOG.md b/CHANGELOG.md index 562474353..3c199be96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog ## Master (Unreleased) -* Add `RSpec/VerifiedDoubleReference` cop. ([@t3h2mas][]) * Fix a false positive for `RSpec/EmptyExampleGroup` when expectations in case statement. ([@ydah][]) +* Add `RSpec/VerifiedDoubleReference` cop. ([@t3h2mas][]) +* Make `RSpec/BeNil` cop configurable with a `be_nil` style and a `be` style. ([@bquorning][]) ## 2.9.0 (2022-02-28) diff --git a/config/default.yml b/config/default.yml index 89a507887..e02a9039e 100644 --- a/config/default.yml +++ b/config/default.yml @@ -155,9 +155,14 @@ RSpec/BeEql: Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEql RSpec/BeNil: - Description: Check that `be_nil` is used instead of `be(nil)`. + Description: Ensures a consistent style is used when matching `nil`. Enabled: pending + EnforcedStyle: be_nil + SupportedStyles: + - be + - be_nil VersionAdded: 2.9.0 + VersionChanged: 2.10.0 Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeNil RSpec/BeforeAfterAll: diff --git a/docs/modules/ROOT/pages/cops_rspec.adoc b/docs/modules/ROOT/pages/cops_rspec.adoc index fb19a5fd0..b6b7faf02 100644 --- a/docs/modules/ROOT/pages/cops_rspec.adoc +++ b/docs/modules/ROOT/pages/cops_rspec.adoc @@ -283,16 +283,20 @@ expect(foo).to be(nil) | Yes | Yes | 2.9.0 -| - +| 2.10.0 |=== -Check that `be_nil` is used instead of `be(nil)`. +Ensures a consistent style is used when matching `nil`. + +You can either use the more specific `be_nil` matcher, or the more +generic `be` matcher with a `nil` argument. -RSpec has a built-in `be_nil` matcher specifically for expecting `nil`. -For consistent specs, we recommend using that instead of `be(nil)`. +This cop can be configured using the `EnforcedStyle` option === Examples +==== `EnforcedStyle: be_nil` (default) + [source,ruby] ---- # bad @@ -302,6 +306,27 @@ expect(foo).to be(nil) expect(foo).to be_nil ---- +==== `EnforcedStyle: be` + +[source,ruby] +---- +# bad +expect(foo).to be_nil + +# good +expect(foo).to be(nil) +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| EnforcedStyle +| `be_nil` +| `be`, `be_nil` +|=== + === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeNil diff --git a/lib/rubocop/cop/rspec/be_nil.rb b/lib/rubocop/cop/rspec/be_nil.rb index 5878a5ff0..a969ff8ed 100644 --- a/lib/rubocop/cop/rspec/be_nil.rb +++ b/lib/rubocop/cop/rspec/be_nil.rb @@ -3,24 +3,39 @@ module RuboCop module Cop module RSpec - # Check that `be_nil` is used instead of `be(nil)`. + # Ensures a consistent style is used when matching `nil`. # - # RSpec has a built-in `be_nil` matcher specifically for expecting `nil`. - # For consistent specs, we recommend using that instead of `be(nil)`. + # You can either use the more specific `be_nil` matcher, or the more + # generic `be` matcher with a `nil` argument. # - # @example + # This cop can be configured using the `EnforcedStyle` option # + # @example `EnforcedStyle: be_nil` (default) # # bad # expect(foo).to be(nil) # # # good # expect(foo).to be_nil # + # @example `EnforcedStyle: be` + # # bad + # expect(foo).to be_nil + # + # # good + # expect(foo).to be(nil) + # class BeNil < Base extend AutoCorrector + include ConfigurableEnforcedStyle - MSG = 'Prefer `be_nil` over `be(nil)`.' - RESTRICT_ON_SEND = %i[be].freeze + BE_MSG = 'Prefer `be(nil)` over `be_nil`.' + BE_NIL_MSG = 'Prefer `be_nil` over `be(nil)`.' + RESTRICT_ON_SEND = %i[be be_nil].freeze + + # @!method be_nil_matcher?(node) + def_node_matcher :be_nil_matcher?, <<-PATTERN + (send nil? :be_nil) + PATTERN # @!method nil_value_expectation?(node) def_node_matcher :nil_value_expectation?, <<-PATTERN @@ -28,9 +43,28 @@ class BeNil < Base PATTERN def on_send(node) + case style + when :be + check_be_style(node) + when :be_nil + check_be_nil_style(node) + end + end + + private + + def check_be_style(node) + return unless be_nil_matcher?(node) + + add_offense(node, message: BE_MSG) do |corrector| + corrector.replace(node.loc.expression, 'be(nil)') + end + end + + def check_be_nil_style(node) return unless nil_value_expectation?(node) - add_offense(node) do |corrector| + add_offense(node, message: BE_NIL_MSG) do |corrector| corrector.replace(node.loc.expression, 'be_nil') end end diff --git a/spec/rubocop/cop/rspec/be_nil_spec.rb b/spec/rubocop/cop/rspec/be_nil_spec.rb index 905d49935..3405f6d75 100644 --- a/spec/rubocop/cop/rspec/be_nil_spec.rb +++ b/spec/rubocop/cop/rspec/be_nil_spec.rb @@ -1,30 +1,67 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::RSpec::BeNil do - it 'registers an offense when using `#be` for `nil` value' do - expect_offense(<<~RUBY) - expect(foo).to be(nil) - ^^^^^^^ Prefer `be_nil` over `be(nil)`. - RUBY - - expect_correction(<<~RUBY) - expect(foo).to be_nil - RUBY + let(:cop_config) do + { 'EnforcedStyle' => enforced_style } end - it 'does not register an offense when using `#be_nil`' do - expect_no_offenses(<<~RUBY) - expect(foo).to be_nil - RUBY + context 'with EnforcedStyle `be_nil`' do + let(:enforced_style) { 'be_nil' } + + it 'registers an offense when using `#be` for `nil` value' do + expect_offense(<<~RUBY) + expect(foo).to be(nil) + ^^^^^^^ Prefer `be_nil` over `be(nil)`. + RUBY + + expect_correction(<<~RUBY) + expect(foo).to be_nil + RUBY + end + + it 'does not register an offense when using `#be_nil`' do + expect_no_offenses(<<~RUBY) + expect(foo).to be_nil + RUBY + end + + it 'does not register an offense when using `#be` with other values' do + expect_no_offenses(<<~RUBY) + expect(foo).to be(true) + expect(foo).to be(false) + expect(foo).to be(1) + expect(foo).to be("yes") + expect(foo).to be(Class.new) + RUBY + end end - it 'does not register an offense when using `#be` with other values' do - expect_no_offenses(<<~RUBY) - expect(foo).to be(true) - expect(foo).to be(false) - expect(foo).to be(1) - expect(foo).to be("yes") - expect(foo).to be(Class.new) - RUBY + context 'with EnforcedStyle `be`' do + let(:enforced_style) { 'be' } + + it 'does not register an offense when using `#be` for `nil` value' do + expect_no_offenses(<<~RUBY) + expect(foo).to be(nil) + RUBY + end + + it 'registers an offense when using `#be_nil`' do + expect_offense(<<~RUBY) + expect(foo).to be_nil + ^^^^^^ Prefer `be(nil)` over `be_nil`. + RUBY + + expect_correction(<<~RUBY) + expect(foo).to be(nil) + RUBY + end + + it 'does not register an offense when using other `#be_*` methods' do + expect_no_offenses(<<~RUBY) + expect(foo).to be_truthy + expect(foo).to be_falsey + expect(foo).to be_fooish + RUBY + end end end