class RuboCop::Cop::RSpec::RepeatedSubjectCall

Checks for repeated calls to subject missing that it is memoized.

@example

# bad
it do
  subject
  expect { subject }.to not_change { A.count }
end

it do
  expect { subject }.to change { A.count }
  expect { subject }.to not_change { A.count }
end

# good
it do
  expect { my_method }.to change { A.count }
  expect { my_method }.to not_change { A.count }
end

# also good
it do
  expect { subject.a }.to change { A.count }
  expect { subject.b }.to not_change { A.count }
end

Constants

MSG

Public Instance Methods

on_top_level_group(node) click to toggle source
# File lib/rubocop/cop/rspec/repeated_subject_call.rb, line 65
def on_top_level_group(node)
  @subjects_by_node = detect_subjects_in_scope(node)

  detect_offenses_in_block(node)
end

Private Instance Methods

detect_offense(subject_node) click to toggle source
# File lib/rubocop/cop/rspec/repeated_subject_call.rb, line 73
def detect_offense(subject_node)
  return if subject_node.chained?
  return if subject_node.parent.send_type?
  return unless (block_node = expect_block(subject_node))

  add_offense(block_node)
end
detect_offenses_in_block(node, subject_names = []) click to toggle source
# File lib/rubocop/cop/rspec/repeated_subject_call.rb, line 85
def detect_offenses_in_block(node, subject_names = [])
  subject_names = [*subject_names, *@subjects_by_node[node]]

  if example?(node)
    return detect_offenses_in_example(node, subject_names)
  end

  node.each_child_node(:send, :def, :block, :begin) do |child|
    detect_offenses_in_block(child, subject_names)
  end
end
detect_offenses_in_example(node, subject_names) click to toggle source
# File lib/rubocop/cop/rspec/repeated_subject_call.rb, line 97
def detect_offenses_in_example(node, subject_names)
  return unless node.body

  subjects_used = Hash.new(false)

  subject_calls(node.body, Set[*subject_names, :subject]).each do |call|
    if subjects_used[call.method_name]
      detect_offense(call)
    else
      subjects_used[call.method_name] = true
    end
  end
end
detect_subjects_in_scope(node) click to toggle source
# File lib/rubocop/cop/rspec/repeated_subject_call.rb, line 111
def detect_subjects_in_scope(node)
  node.each_descendant(:block).with_object({}) do |child, h|
    subject?(child) do |name|
      outer_example_group = child.each_ancestor(:block).find do |a|
        example_group?(a)
      end

      (h[outer_example_group] ||= []) << name
    end
  end
end
expect_block(node) click to toggle source
# File lib/rubocop/cop/rspec/repeated_subject_call.rb, line 81
def expect_block(node)
  node.each_ancestor(:block).find { |block| block.method?(:expect) }
end