Class: RuboCop::Cop::RSpec::OverwrittenLet

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

Overview

Forbids overriding a ‘let`/`let!` that is already defined in an outer example group. Shadowing an ancestor’s ‘let` makes it ambiguous which value applies in a given example.

A ‘let` that is specific to a single scenario is allowed, even when a sibling context defines a `let` of the same name — siblings are separate scopes and do not override each other. Only an inner `let` that shadows an `let` from an ancestor example group is flagged.

Examples:

# bad — inner let shadows the outer one
let(:user) { create(:user) }

context 'when admin' do
  let(:user) { create(:user, :admin) }
end

# good — scenario-specific let, no ancestor defines it
context 'when admin' do
  let(:user) { create(:user, :admin) }
end

context 'when regular' do
  let(:user) { create(:user) }
end

Constant Summary collapse

MSG =
'Do not override the outer `let` `%<name>s`; use a distinct name or set the value in `before`.'
LET_METHODS =
%i[let let!].freeze
EXAMPLE_GROUP_METHODS =
%i[describe context].freeze

Instance Method Summary collapse

Instance Method Details

#on_send(node) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/rubocop/cop/rspec/overwritten_let.rb', line 40

def on_send(node)
  return unless LET_METHODS.include?(node.method_name) && node.receiver.nil?

  name = let_name(node)
  return unless name

  immediate_group = node.each_ancestor(:block).find { |block| example_group?(block) }
  return unless immediate_group
  return unless outer_groups(immediate_group).any? { |group| defines_let?(group, name) }

  add_offense(node.loc.selector, message: format(MSG, name: name))
end