diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d7652184..b44dd082a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Mark `RSpec/IncludeExamples` as `SafeAutoCorrect: false`. ([@yujideveloper]) - Fix a false positive for `RSpec/LeakyConstantDeclaration` when defining constants in explicit namespaces. ([@naveg]) +- Offend `allow_any_instance_of` and `expect_any_instance_of` in `RSpec/SubjectStub`. ([@lovro-bikic]) ## 3.6.0 (2025-04-18) diff --git a/docs/modules/ROOT/pages/cops_rspec.adoc b/docs/modules/ROOT/pages/cops_rspec.adoc index 40c7757ac..d112e0afa 100644 --- a/docs/modules/ROOT/pages/cops_rspec.adoc +++ b/docs/modules/ROOT/pages/cops_rspec.adoc @@ -6150,6 +6150,17 @@ describe Article do end end +# bad +describe Article do + subject { Article } + + it 'indicates that the author is unknown' do + allow_any_instance_of(subject).to receive(:author).and_return(nil) + + expect(subject.new.description).to include('by an unknown author') + end +end + # good describe Article do subject(:article) { Article.new(author: nil) } diff --git a/lib/rubocop/cop/rspec/subject_stub.rb b/lib/rubocop/cop/rspec/subject_stub.rb index 273e4fea9..f0d497a2a 100644 --- a/lib/rubocop/cop/rspec/subject_stub.rb +++ b/lib/rubocop/cop/rspec/subject_stub.rb @@ -38,6 +38,17 @@ module RSpec # end # end # + # # bad + # describe Article do + # subject { Article } + # + # it 'indicates that the author is unknown' do + # allow_any_instance_of(subject).to receive(:author).and_return(nil) + # + # expect(subject.new.description).to include('by an unknown author') + # end + # end + # # # good # describe Article do # subject(:article) { Article.new(author: nil) } @@ -93,11 +104,13 @@ class SubjectStub < Base # expect(foo).to receive(:bar) # expect(foo).to receive(:bar).with(1) # expect(foo).to receive(:bar).with(1).and_return(2) + # expect_any_instance_of(foo).to receive(:bar) + # allow_any_instance_of(foo).to receive(:bar) # def_node_matcher :message_expectation?, <<~PATTERN (send { - (send nil? { :expect :allow } (send nil? %)) + (send nil? { :expect :allow :expect_any_instance_of :allow_any_instance_of } (send nil? %)) (send nil? :is_expected) } #Runners.all diff --git a/spec/rubocop/cop/rspec/subject_stub_spec.rb b/spec/rubocop/cop/rspec/subject_stub_spec.rb index 575f27f76..c25c4363a 100644 --- a/spec/rubocop/cop/rspec/subject_stub_spec.rb +++ b/spec/rubocop/cop/rspec/subject_stub_spec.rb @@ -38,6 +38,23 @@ RUBY end + it 'flags when any instance of subject is stubbed' do + expect_offense(<<~RUBY) + describe Foo do + subject(:foo) { described_class } + + before do + allow_any_instance_of(foo).to receive(:bar).and_return(baz) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not stub methods of the object under test. + end + + it 'uses expect twice' do + expect(foo.bar).to eq(baz) + end + end + RUBY + end + it 'flags when subject is mocked' do expect_offense(<<~RUBY) describe Foo do @@ -126,6 +143,19 @@ RUBY end + it 'flags when any instance of subject is mocked' do + expect_offense(<<~RUBY) + describe Foo do + subject(:foo) { described_class } + + it 'uses named subject' do + expect_any_instance_of(foo).to receive(:bar).and_return(baz) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not stub methods of the object under test. + end + end + RUBY + end + it 'flags one-line expectation syntax' do expect_offense(<<~RUBY) describe Foo do