Class: RuboCop::Cop::RSpec::DescribedClass

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Includes:
ConfigurableEnforcedStyle, Namespace
Defined in:
lib/rubocop/cop/rspec/described_class.rb

Overview

Checks that tests use ‘described_class`.

If the first argument of describe is a class, the class is exposed to each example via described_class.

This cop can be configured using the ‘EnforcedStyle`, `SkipBlocks` and `OnlyStaticConstants` options. `OnlyStaticConstants` is only relevant when `EnforcedStyle` is `described_class`.

There’s a known caveat with rspec-rails’s ‘controller` helper that runs its block in a different context, and `described_class` is not available to it. `SkipBlocks` option excludes detection in all non-RSpec related blocks.

To narrow down this setting to only a specific directory, it is possible to use an overriding configuration file local to that directory.

Examples:

‘EnforcedStyle: described_class` (default)

# bad
describe MyClass do
  subject { MyClass.do_something }
end

# good
describe MyClass do
  subject { described_class.do_something }
end

‘OnlyStaticConstants: true` (default)

# good
describe MyClass do
  subject { MyClass::CONSTANT }
end

‘OnlyStaticConstants: false`

# bad
describe MyClass do
  subject { MyClass::CONSTANT }
end

‘EnforcedStyle: explicit`

# bad
describe MyClass do
  subject { described_class.do_something }
end

# good
describe MyClass do
  subject { MyClass.do_something }
end

‘SkipBlocks: true`

# spec/controllers/.rubocop.yml
# RSpec/DescribedClass:
#   SkipBlocks: true

# acceptable
describe MyConcern do
  controller(ApplicationController) do
    include MyConcern
  end
end

Constant Summary collapse

DESCRIBED_CLASS =
'described_class'
MSG =
'Use `%<replacement>s` instead of `%<src>s`.'

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

#common_instance_exec_closure?(node) ⇒ Object



80
81
82
# File 'lib/rubocop/cop/rspec/described_class.rb', line 80

def_node_matcher :common_instance_exec_closure?, <<~PATTERN
  (block (send (const nil? {:Class :Module :Struct}) :new ...) ...)
PATTERN

#contains_described_class?(node) ⇒ Object



97
98
# File 'lib/rubocop/cop/rspec/described_class.rb', line 97

def_node_search :contains_described_class?,
'(send nil? :described_class)'

#described_constant(node) ⇒ Object



92
93
94
# File 'lib/rubocop/cop/rspec/described_class.rb', line 92

def_node_matcher :described_constant, <<~PATTERN
  (block (send _ :describe $(const ...) ...) (args) $_)
PATTERN

#on_block(node) ⇒ Object

rubocop:disable InternalAffairs/NumblockHandler



100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/rubocop/cop/rspec/described_class.rb', line 100

def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
  # In case the explicit style is used, we need to remember what's
  # being described.
  @described_class, body = described_constant(node)

  return unless body

  find_usage(body) do |match|
    msg = message(match.const_name)
    add_offense(match, message: msg) do |corrector|
      autocorrect(corrector, match)
    end
  end
end

#rspec_block?(node) ⇒ Object



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

def_node_matcher :rspec_block?,
'({block numblock} (send #rspec? #ALL.all ...) ...)'

#scope_changing_syntax?(node) ⇒ Object



89
# File 'lib/rubocop/cop/rspec/described_class.rb', line 89

def_node_matcher :scope_changing_syntax?, '{def class module}'