Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
mockdeep committed Jun 13, 2020
1 parent a64e307 commit 5d51ade
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 113 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 for over-usage of `let` and `subject` blocks in example groups.
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
34 changes: 24 additions & 10 deletions lib/rubocop/cop/rspec/memoized_helpers_in_example_group.rb
Expand Up @@ -3,8 +3,9 @@
module RuboCop
module Cop
module RSpec
# Checks for usage of `let` blocks in specs.
# Checks for over-usage of `let` and `subject` blocks in example groups.
#
# The maximum count can be configured.
# 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`
Expand Down Expand Up @@ -55,22 +56,35 @@ module RSpec
# end
#
class MemoizedHelpersInExampleGroup < Cop
MSG = 'Avoid using `%<method>s` ' \
'– use a method call or local variable instead.'
MSG = 'Too many memoized helpers used. [%<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 example_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
20 changes: 20 additions & 0 deletions lib/rubocop/rspec/example_group.rb
Expand Up @@ -14,6 +14,10 @@ class ExampleGroup < Concept
ExampleGroups::ALL + SharedGroups::ALL + Includes::ALL
).block_pattern

def lets
lets_in_scope(node)
end

def subjects
subjects_in_scope(node)
end
Expand All @@ -34,6 +38,12 @@ def subjects_in_scope(node)
end
end

def lets_in_scope(node)
node.each_child_node.flat_map do |child|
find_lets(child)
end
end

def find_subjects(node)
return [] if scope_change?(node)

Expand All @@ -44,6 +54,16 @@ def find_subjects(node)
end
end

def find_lets(node)
return [] if scope_change?(node)

if let?(node)
[node]
else
lets_in_scope(node)
end
end

def hooks_in_scope(node)
node.each_child_node.flat_map do |child|
find_hooks(child)
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
144 changes: 73 additions & 71 deletions manual/cops_rspec.md
Expand Up @@ -1890,6 +1890,79 @@ end

* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup)

## RSpec/MemoizedHelpersInExampleGroup

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Disabled | Yes | No | - | -

Checks for over-usage of `let` and `subject` blocks in example groups.

The maximum count can be configured.
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`

### Examples

```ruby
# bad
describe MyClass do
let(:foo) { [] }
it { expect(foo).to be_empty }
end

describe MyClass do
subject(:foo) { [] }
it { expect(foo).to be_empty }
end

# good
describe MyClass do
it do
foo = []
expect(foo).to be_empty
end
end
```
#### with AllowSubject configuration

```ruby
# rubocop.yml
# RSpec/MemoizedHelpersInExampleGroup:
# AllowSubject: true

# bad
describe MyClass do
let(:foo) { [] }
it { expect(foo).to be_empty }
end

# good
describe MyClass do
subject(:foo) { [] }
it { expect(foo).to be_empty }
end

describe MyClass do
it do
foo = []
expect(foo).to be_empty
end
end
```

### Configurable attributes

Name | Default value | Configurable values
--- | --- | ---
AllowSubject | `false` | Boolean
Max | `5` | Integer

### References

* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelpersInExampleGroup](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelpersInExampleGroup)

## RSpec/MessageChain

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down Expand Up @@ -2322,77 +2395,6 @@ Max | `3` | Integer

* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NestedGroups](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NestedGroups)

## RSpec/MemoizedHelpersInExampleGroup

Enabled by default | Supports autocorrection
--- | ---
Disabled | No

Checks for usage of `let` blocks in specs.

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`

### Examples

```ruby
# bad
describe MyClass do
let(:foo) { [] }
it { expect(foo).to be_empty }
end

describe MyClass do
subject(:foo) { [] }
it { expect(foo).to be_empty }
end

# good
describe MyClass do
it do
foo = []
expect(foo).to be_empty
end
end
```
#### with AllowSubject configuration

```ruby
# rubocop.yml
# RSpec/MemoizedHelpersInExampleGroup:
# AllowSubject: true

# bad
describe MyClass do
let(:foo) { [] }
it { expect(foo).to be_empty }
end

# good
describe MyClass do
subject(:foo) { [] }
it { expect(foo).to be_empty }
end

describe MyClass do
it do
foo = []
expect(foo).to be_empty
end
end
```

### Configurable attributes

Name | Default value | Configurable values
--- | --- | ---
AllowSubject | `false` | Boolean

### References

* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelpersInExampleGroup](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelpersInExampleGroup)

## RSpec/NotToNot

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down

0 comments on commit 5d51ade

Please sign in to comment.