Class: RuboCop::Cop::RSpec::SubjectStub

Inherits:
Base
  • Object
show all
Includes:
TopLevelGroup
Defined in:
lib/rubocop/cop/rspec/subject_stub.rb

Overview

Checks for stubbed test subjects.

Checks nested subject stubs for innermost subject definition when subject is also defined in parent example groups.

Examples:

# bad
describe Article do
  subject(:article) { Article.new }

  it 'indicates that the author is unknown' do
    allow(article).to receive(:author).and_return(nil)
    expect(article.description).to include('by an unknown author')
  end
end

# bad
describe Article do
  subject(:foo) { Article.new }

  context 'nested subject' do
    subject(:article) { Article.new }

    it 'indicates that the author is unknown' do
      allow(article).to receive(:author).and_return(nil)
      expect(article.description).to include('by an unknown author')
    end
  end
end

# good
describe Article do
  subject(:article) { Article.new(author: nil) }

  it 'indicates that the author is unknown' do
    expect(article.description).to include('by an unknown author')
  end
end

See Also:

Constant Summary collapse

MSG =
'Do not stub methods of the object under test.'

Instance Method Summary collapse

Methods included from TopLevelGroup

#on_new_investigation, #top_level_groups

Methods inherited from Base

inherited, #on_new_investigation

Methods included from RSpec::Language

#example?, #example_group?, #example_group_with_body?, #explicit_rspec?, #hook?, #include?, #rspec?, #shared_group?, #spec_group?

Instance Method Details

#let?(node) ⇒ Object

Find a memoized helper



80
81
82
83
84
# File 'lib/rubocop/cop/rspec/subject_stub.rb', line 80

def_node_matcher :let?, <<~PATTERN
  (block
    (send nil? :let (sym $_)
    ) args ...)
PATTERN

#message_expectation?(node, method_name) ⇒ Object

Match ‘allow` and `expect(…).to receive`

Examples:

source that matches

allow(foo).to  receive(:bar)
allow(foo).to  receive(:bar).with(1)
allow(foo).to  receive(:bar).with(1).and_return(2)
expect(foo).to receive(:bar)
expect(foo).to receive(:bar).with(1)
expect(foo).to receive(:bar).with(1).and_return(2)


97
98
99
100
101
102
103
104
105
106
# File 'lib/rubocop/cop/rspec/subject_stub.rb', line 97

def_node_matcher :message_expectation?, <<~PATTERN
  (send
    {
      (send nil? { :expect :allow } (send nil? %))
      (send nil? :is_expected)
    }
    #Runners.all
    #message_expectation_matcher?
  )
PATTERN

#message_expectation_matcher?(node) ⇒ Object



109
110
111
112
113
# File 'lib/rubocop/cop/rspec/subject_stub.rb', line 109

def_node_search :message_expectation_matcher?, <<~PATTERN
  (send nil? {
    :receive :receive_messages :receive_message_chain :have_received
    } ...)
PATTERN

#on_top_level_group(node) ⇒ Object



115
116
117
118
119
120
121
122
# File 'lib/rubocop/cop/rspec/subject_stub.rb', line 115

def on_top_level_group(node)
  @explicit_subjects = find_all_explicit(node) { |n| subject?(n) }
  @subject_overrides = find_all_explicit(node) { |n| let?(n) }

  find_subject_expectations(node) do |stub|
    add_offense(stub)
  end
end

#subject?(node) {|Symbol| ... } ⇒ Object

Find a named or unnamed subject definition

Examples:

anonymous subject

subject?(parse('subject { foo }').ast) do |name|
  name # => :subject
end

named subject

subject?(parse('subject(:thing) { foo }').ast) do |name|
  name # => :thing
end

Parameters:

  • node (RuboCop::AST::Node)

Yields:

  • (Symbol)

    subject name



71
72
73
74
75
76
# File 'lib/rubocop/cop/rspec/subject_stub.rb', line 71

def_node_matcher :subject?, <<~PATTERN
  (block
    (send nil?
      { #Subjects.all (sym $_) | $#Subjects.all }
    ) args ...)
PATTERN