Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
mockdeep committed Aug 2, 2020
1 parent 234e7db commit c2c21a8
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 129 deletions.
13 changes: 7 additions & 6 deletions config/default.yml
Expand Up @@ -347,6 +347,13 @@ RSpec/LetSetup:
VersionAdded: '1.7'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup

RSpec/MemoizedHelpersInExampleGroup:
Description: Checks if example groups contain too many `let` and `subject` calls.
Enabled: false
AllowSubject: false
Max: 5
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelpersInExampleGroup

RSpec/MessageChain:
Description: Check that chains of messages are not being stubbed.
Enabled: true
Expand Down Expand Up @@ -415,12 +422,6 @@ RSpec/NestedGroups:
VersionChanged: '1.10'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NestedGroups

RSpec/MemoizedHelpersInExampleGroup:
Description: Checks for usage of `let` blocks in specs.
Enabled: false
AllowSubject: false
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelpersInExampleGroup

RSpec/NotToNot:
Description: Checks for consistent method usage for negating expectations.
Enabled: true
Expand Down
108 changes: 82 additions & 26 deletions lib/rubocop/cop/rspec/memoized_helpers_in_example_group.rb
Expand Up @@ -3,29 +3,55 @@
module RuboCop
module Cop
module RSpec
# Checks for usage of `let` blocks in specs.
# Checks if example groups contain too many `let` and `subject` calls.
#
# This cop can be configured with the option `AllowSubject` which
# will configure the cop to only register offenses on explicit calls to
# `let` and not calls to `subject`
# 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) { [] }
# it { expect(foo).to be_empty }
# let(:bar) { [] }
# let!(:booger) { [] }
# subject { {} }
# subject(:wat) { {} }
# subject!(:boo) { {} }
# end
#
# describe MyClass do
# subject(:foo) { [] }
# it { expect(foo).to be_empty }
# let(:foo) { [] }
# let(:bar) { [] }
#
# context 'when stuff' do
# let!(:booger) { [] }
# subject { {} }
# subject(:wat) { {} }
# subject!(:boo) { {} }
# end
# end
#
# # good
# describe MyClass do
# it do
# foo = []
# expect(foo).to be_empty
# let(:foo) { [] }
# let!(:booger) { [] }
# subject { {} }
# subject(:wat) { {} }
# subject!(:boo) { {} }
# end
#
# describe MyClass do
# context 'when stuff' do
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:booger) { [] }
# end
#
# context 'when other stuff' do
# subject { {} }
# subject(:wat) { {} }
# subject!(:boo) { {} }
# end
# end
#
Expand All @@ -38,39 +64,69 @@ module RSpec
# # bad
# describe MyClass do
# let(:foo) { [] }
# it { expect(foo).to be_empty }
# let(:bar) { [] }
# let!(:booger) { [] }
# let(:subject) { {} }
# let(:wat) { {} }
# let!(:boo) { {} }
# end
#
# # good
# describe MyClass do
# subject(:foo) { [] }
# it { expect(foo).to be_empty }
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:booger) { [] }
# subject { {} }
# subject(:wat) { {} }
# subject!(:boo) { {} }
# end
#
# @example with Max configuration
#
# # rubocop.yml
# # RSpec/MemoizedHelpersInExampleGroup:
# # Max: 0
#
# # bad
# describe MyClass do
# let(:foo) { [] }
# end
#
# # good
# describe MyClass do
# it do
# foo = []
# expect(foo).to be_empty
# end
# def foo; []; end
# end
#
class MemoizedHelpersInExampleGroup < Cop
MSG = 'Avoid using `%<method>s` ' \
'– use a method call or local variable instead.'
MSG = 'Example has too many memoized helpers [%<count>d/%<max>d]'

def_node_matcher :let?, Helpers::ALL.send_pattern
def_node_matcher :subject?, Subject::ALL.send_pattern
def on_block(node)
return unless spec_group?(node)

def on_send(node)
if subject?(node) && !allow_subject?
add_offense(node, message: format(MSG, method: 'subject'))
elsif let?(node)
add_offense(node, message: format(MSG, method: 'let'))
count = count_helpers(node)

node.each_ancestor(:block) do |ancestor|
count += count_helpers(ancestor)
end

return if count <= max

add_offense(node, message: format(MSG, count: count, max: max))
end

private

def count_helpers(node)
example_group = RuboCop::RSpec::ExampleGroup.new(node)
count = example_group.lets.count
count += example_group.subjects.count unless allow_subject?
count
end

def max
cop_config['Max']
end

def allow_subject?
cop_config['AllowSubject']
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/rspec_cops.rb
Expand Up @@ -59,6 +59,7 @@
require_relative 'rspec/leaky_constant_declaration'
require_relative 'rspec/let_before_examples'
require_relative 'rspec/let_setup'
require_relative 'rspec/memoized_helpers_in_example_group'
require_relative 'rspec/message_chain'
require_relative 'rspec/message_expectation'
require_relative 'rspec/message_spies'
Expand All @@ -68,7 +69,6 @@
require_relative 'rspec/multiple_subjects'
require_relative 'rspec/named_subject'
require_relative 'rspec/nested_groups'
require_relative 'rspec/no_let'
require_relative 'rspec/not_to_not'
require_relative 'rspec/overwriting_setup'
require_relative 'rspec/pending'
Expand Down
2 changes: 1 addition & 1 deletion manual/cops.md
Expand Up @@ -58,6 +58,7 @@
* [RSpec/LeakyConstantDeclaration](cops_rspec.md#rspecleakyconstantdeclaration)
* [RSpec/LetBeforeExamples](cops_rspec.md#rspecletbeforeexamples)
* [RSpec/LetSetup](cops_rspec.md#rspecletsetup)
* [RSpec/MemoizedHelpersInExampleGroup](cops_rspec.md#rspecmemoizedhelpersinexamplegroup)
* [RSpec/MessageChain](cops_rspec.md#rspecmessagechain)
* [RSpec/MessageExpectation](cops_rspec.md#rspecmessageexpectation)
* [RSpec/MessageSpies](cops_rspec.md#rspecmessagespies)
Expand All @@ -67,7 +68,6 @@
* [RSpec/MultipleSubjects](cops_rspec.md#rspecmultiplesubjects)
* [RSpec/NamedSubject](cops_rspec.md#rspecnamedsubject)
* [RSpec/NestedGroups](cops_rspec.md#rspecnestedgroups)
* [RSpec/MemoizedHelpersInExampleGroup](cops_rspec.md#rspecnolet)
* [RSpec/NotToNot](cops_rspec.md#rspecnottonot)
* [RSpec/OverwritingSetup](cops_rspec.md#rspecoverwritingsetup)
* [RSpec/Pending](cops_rspec.md#rspecpending)
Expand Down

0 comments on commit c2c21a8

Please sign in to comment.