class RuboCop::Cop::RSpec::MultipleMemoizedHelpers

Checks if example groups contain too many ‘let` and `subject` calls.

This cop is configurable using the ‘Max` option and the `AllowSubject` which will configure the cop to only register offenses on calls to `let` and not calls to `subject`.

@example

# bad
describe MyClass do
  let(:foo) { [] }
  let(:bar) { [] }
  let!(:baz) { [] }
  let(:qux) { [] }
  let(:quux) { [] }
  let(:quuz) { {} }
end

describe MyClass do
  let(:foo) { [] }
  let(:bar) { [] }
  let!(:baz) { [] }

  context 'when stuff' do
    let(:qux) { [] }
    let(:quux) { [] }
    let(:quuz) { {} }
  end
end

# good
describe MyClass do
  let(:bar) { [] }
  let!(:baz) { [] }
  let(:qux) { [] }
  let(:quux) { [] }
  let(:quuz) { {} }
end

describe MyClass do
  context 'when stuff' do
    let(:foo) { [] }
    let(:bar) { [] }
    let!(:booger) { [] }
  end

  context 'when other stuff' do
    let(:qux) { [] }
    let(:quux) { [] }
    let(:quuz) { {} }
  end
end

@example when disabling AllowSubject configuration

# rubocop.yml
# RSpec/MultipleMemoizedHelpers:
#   AllowSubject: false

# bad - `subject` counts towards memoized helpers
describe MyClass do
  subject { {} }
  let(:foo) { [] }
  let(:bar) { [] }
  let!(:baz) { [] }
  let(:qux) { [] }
  let(:quux) { [] }
end

@example with Max configuration

# rubocop.yml
# RSpec/MultipleMemoizedHelpers:
#   Max: 1

# bad
describe MyClass do
  let(:foo) { [] }
  let(:bar) { [] }
end

Constants

MSG

Attributes

example_group_memoized_helpers[R]

Public Instance Methods

on_block(node) click to toggle source
# File lib/rubocop/cop/rspec/multiple_memoized_helpers.rb, line 91
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
  return unless spec_group?(node)

  count = all_helpers(node).uniq.count

  return if count <= max

  self.max = count
  add_offense(node, message: format(MSG, count: count, max: max))
end
on_new_investigation() click to toggle source
# File lib/rubocop/cop/rspec/multiple_memoized_helpers.rb, line 102
def on_new_investigation
  super
  @example_group_memoized_helpers = {}
end

Private Instance Methods

all_helpers(node) click to toggle source
# File lib/rubocop/cop/rspec/multiple_memoized_helpers.rb, line 111
def all_helpers(node)
  helpers(node) +
    node.each_ancestor(:block).flat_map { |ancestor| helpers(ancestor) }
end
allow_subject?() click to toggle source
# File lib/rubocop/cop/rspec/multiple_memoized_helpers.rb, line 141
def allow_subject?
  cop_config['AllowSubject']
end
helpers(node) click to toggle source
# File lib/rubocop/cop/rspec/multiple_memoized_helpers.rb, line 116
def helpers(node)
  @example_group_memoized_helpers[node] ||=
    variable_nodes(node).map do |variable_node|
      if variable_node.block_type?
        variable_definition?(variable_node.send_node)
      else # block-pass (`let(:foo, &bar)`)
        variable_definition?(variable_node)
      end
    end
end
max() click to toggle source
# File lib/rubocop/cop/rspec/multiple_memoized_helpers.rb, line 137
def max
  cop_config['Max']
end
variable_nodes(node) click to toggle source
# File lib/rubocop/cop/rspec/multiple_memoized_helpers.rb, line 127
def variable_nodes(node)
  example_group = RuboCop::RSpec::ExampleGroup.new(node)

  if allow_subject?
    example_group.lets
  else
    example_group.lets + example_group.subjects
  end
end