class RuboCop::Cop::RSpec::MultipleExpectations

Checks if examples contain too many ‘expect` calls.

@see betterspecs.org/#single Single expectation test

This cop is configurable using the ‘Max` option and works with `–auto-gen-config`.

@example

# bad
describe UserCreator do
  it 'builds a user' do
    expect(user.name).to eq("John")
    expect(user.age).to eq(22)
  end
end

# good
describe UserCreator do
  it 'sets the users name' do
    expect(user.name).to eq("John")
  end

  it 'sets the users age' do
    expect(user.age).to eq(22)
  end
end

@example ‘aggregate_failures: true` (default)

# good - the cop ignores when RSpec aggregates failures
describe UserCreator do
  it 'builds a user', :aggregate_failures do
    expect(user.name).to eq("John")
    expect(user.age).to eq(22)
  end
end

@example ‘aggregate_failures: false`

# Detected as an offense
describe UserCreator do
  it 'builds a user', aggregate_failures: false do
    expect(user.name).to eq("John")
    expect(user.age).to eq(22)
  end
end

@example ‘Max: 1` (default)

# bad
describe UserCreator do
  it 'builds a user' do
    expect(user.name).to eq("John")
    expect(user.age).to eq(22)
  end
end

@example ‘Max: 2`

# good
describe UserCreator do
  it 'builds a user' do
    expect(user.name).to eq("John")
    expect(user.age).to eq(22)
  end
end

Constants

ANYTHING
MSG
TRUE_NODE

Public Instance Methods

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

  return if example_with_aggregate_failures?(node)

  expectations_count = to_enum(:find_expectation, node).count

  return if expectations_count <= max_expectations

  self.max = expectations_count

  flag_example(node, expectation_count: expectations_count)
end

Private Instance Methods

example_with_aggregate_failures?(example_node) click to toggle source
# File lib/rubocop/cop/rspec/multiple_expectations.rb, line 109
def example_with_aggregate_failures?(example_node)
  node_with_aggregate_failures = find_aggregate_failures(example_node)
  return false unless node_with_aggregate_failures

  aggregate_failures?(node_with_aggregate_failures, TRUE_NODE)
end
find_aggregate_failures(example_node) click to toggle source
# File lib/rubocop/cop/rspec/multiple_expectations.rb, line 116
def find_aggregate_failures(example_node)
  example_node.send_node.each_ancestor(:block)
    .find { |block_node| aggregate_failures?(block_node, ANYTHING) }
end
find_expectation(node) { || ... } click to toggle source
# File lib/rubocop/cop/rspec/multiple_expectations.rb, line 121
def find_expectation(node, &block)
  yield if expect?(node) || aggregate_failures_block?(node)

  # do not search inside of aggregate_failures block
  return if aggregate_failures_block?(node)

  node.each_child_node do |child|
    find_expectation(child, &block)
  end
end
flag_example(node, expectation_count:) click to toggle source
# File lib/rubocop/cop/rspec/multiple_expectations.rb, line 132
def flag_example(node, expectation_count:)
  add_offense(
    node.send_node,
    message: format(
      MSG,
      total: expectation_count,
      max: max_expectations
    )
  )
end
max_expectations() click to toggle source
# File lib/rubocop/cop/rspec/multiple_expectations.rb, line 143
def max_expectations
  Integer(cop_config.fetch('Max', 1))
end