Class: RuboCop::Cop::RSpec::NamedSubject

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

Overview

Checks for explicitly referenced test subjects.

RSpec lets you declare an “implicit subject” using ‘subject { … }` which allows for tests like `it { is_expected.to be_valid }`. If you need to reference your test subject you should explicitly name it using `subject(:your_subject_name) { … }`. Your test subjects should be the most important object in your tests so they deserve a descriptive name.

This cop can be configured in your configuration using ‘EnforcedStyle`, and `IgnoreSharedExamples` which will not report offenses for implicit subjects in shared example groups.

Examples:

‘EnforcedStyle: always` (default)

# bad
RSpec.describe User do
  subject { described_class.new }

  it 'is valid' do
    expect(subject.valid?).to be(true)
  end
end

# good
RSpec.describe User do
  subject(:user) { described_class.new }

  it 'is valid' do
    expect(user.valid?).to be(true)
  end
end

# also good
RSpec.describe User do
  subject(:user) { described_class.new }

  it { is_expected.to be_valid }
end

‘EnforcedStyle: named_only`

# bad
RSpec.describe User do
  subject(:user) { described_class.new }

  it 'is valid' do
    expect(subject.valid?).to be(true)
  end
end

# good
RSpec.describe User do
  subject(:user) { described_class.new }

  it 'is valid' do
    expect(user.valid?).to be(true)
  end
end

# also good
RSpec.describe User do
  subject { described_class.new }

  it { is_expected.to be_valid }
end

# acceptable
RSpec.describe User do
  subject { described_class.new }

  it 'is valid' do
    expect(subject.valid?).to be(true)
  end
end

Constant Summary collapse

MSG =
'Name your test subject if you need to reference it explicitly.'

Instance Method Summary collapse

Methods inherited from Base

inherited, #on_new_investigation

Methods included from RSpec::Language

#example?, #example_group?, #example_group_with_body?, #explicit_rspec?, #hook?, #include?, #let?, #rspec?, #shared_group?, #spec_group?, #subject?

Instance Method Details

#example_or_hook_block?(node) ⇒ Object



85
86
87
# File 'lib/rubocop/cop/rspec/named_subject.rb', line 85

def_node_matcher :example_or_hook_block?, <<~PATTERN
  (block (send nil? {#Examples.all #Hooks.all} ...) ...)
PATTERN

#on_block(node) ⇒ Object

rubocop:disable InternalAffairs/NumblockHandler



97
98
99
100
101
102
103
104
105
# File 'lib/rubocop/cop/rspec/named_subject.rb', line 97

def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
  if !example_or_hook_block?(node) || ignored_shared_example?(node)
    return
  end

  subject_usage(node) do |subject_node|
    check_explicit_subject(subject_node)
  end
end

#shared_example?(node) ⇒ Object



90
91
92
# File 'lib/rubocop/cop/rspec/named_subject.rb', line 90

def_node_matcher :shared_example?, <<~PATTERN
  (block (send #rspec? #SharedGroups.examples ...) ...)
PATTERN

#subject_usage(node) ⇒ Object



95
# File 'lib/rubocop/cop/rspec/named_subject.rb', line 95

def_node_search :subject_usage, '$(send nil? :subject)'