class RuboCop::Cop::RSpec::SubjectStub

Checks for stubbed test subjects.

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

@see robots.thoughtbot.com/don-t-stub-the-system-under-test @see penelope.zone/2015/12/27/introducing-rspec-smells-and-where-to-find-them.html#smell-1-stubjec

@example

# 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

Constants

MSG

Public Instance Methods

on_top_level_group(node) click to toggle source
# 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

Private Instance Methods

find_all_explicit(node) { |child| ... } click to toggle source
# File lib/rubocop/cop/rspec/subject_stub.rb, line 126
def find_all_explicit(node)
  node.each_descendant(:block).with_object({}) do |child, h|
    name = yield(child)
    next unless name

    outer_example_group = child.each_ancestor(:block).find do |a|
      example_group?(a)
    end

    h[outer_example_group] ||= []
    h[outer_example_group] << name
  end
end
find_subject_expectations(node, subject_names = []) { |node| ... } click to toggle source
# File lib/rubocop/cop/rspec/subject_stub.rb, line 140
def find_subject_expectations(node, subject_names = [], &block)
  subject_names = [*subject_names, *@explicit_subjects[node]]
  subject_names -= @subject_overrides[node] if @subject_overrides[node]

  names = Set[*subject_names, :subject]
  expectation_detected = message_expectation?(node, names)
  return yield(node) if expectation_detected

  node.each_child_node(:send, :def, :block, :begin) do |child|
    find_subject_expectations(child, subject_names, &block)
  end
end