Skip to content

Commit

Permalink
Add NoLet cop
Browse files Browse the repository at this point in the history
  • Loading branch information
mockdeep authored and pirj committed Aug 6, 2020
1 parent 39cea64 commit 98ecb7e
Show file tree
Hide file tree
Showing 7 changed files with 390 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
* Add `RSpec/RepeatedExampleGroupDescription` cop. ([@lazycoder9][])
* Add block name and other lines to `RSpec/ScatteredSetup` message. ([@elebow][])
* Fix `RSpec/RepeatedDescription` to take into account example metadata. ([@lazycoder9][])
* Add `RSpec/MemoizedHelpersInExampleGroup` cop. ([@mockdeep][])

## 1.37.1 (2019-12-16)

Expand Down Expand Up @@ -539,3 +540,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
[@harrylewis]: https://github.com/harrylewis
[@elliterate]: https://github.com/elliterate
[@mlarraz]: https://github.com/mlarraz
[@mockdeep]: https://github.com/mockdeep
7 changes: 7 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
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
136 changes: 136 additions & 0 deletions lib/rubocop/cop/rspec/memoized_helpers_in_example_group.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Checks if example groups contain too many `let` and `subject` calls.
#
# 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) { [] }
# let(:bar) { [] }
# let!(:booger) { [] }
# subject { {} }
# subject(:wat) { {} }
# subject!(:boo) { {} }
# end
#
# describe MyClass do
# let(:foo) { [] }
# let(:bar) { [] }
#
# context 'when stuff' do
# let!(:booger) { [] }
# subject { {} }
# subject(:wat) { {} }
# subject!(:boo) { {} }
# end
# end
#
# # good
# describe MyClass do
# 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
#
# @example with AllowSubject configuration
#
# # rubocop.yml
# # RSpec/MemoizedHelpersInExampleGroup:
# # AllowSubject: true
#
# # bad
# describe MyClass do
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:booger) { [] }
# let(:subject) { {} }
# let(:wat) { {} }
# let!(:boo) { {} }
# end
#
# # good
# describe MyClass do
# 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
# def foo; []; end
# end
#
class MemoizedHelpersInExampleGroup < Cop
MSG = 'Example has too many memoized helpers [%<count>d/%<max>d]'

def on_block(node)
return unless spec_group?(node)

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
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/rspec_cops.rb
Original file line number Diff line number Diff line change
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 Down
1 change: 1 addition & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
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 Down
116 changes: 116 additions & 0 deletions manual/cops_rspec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1890,6 +1890,122 @@ 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 if example groups contain too many `let` and `subject` calls.

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`.

### Examples

```ruby
# bad
describe MyClass do
let(:foo) { [] }
let(:bar) { [] }
let!(:booger) { [] }
subject { {} }
subject(:wat) { {} }
subject!(:boo) { {} }
end

describe MyClass do
let(:foo) { [] }
let(:bar) { [] }

context 'when stuff' do
let!(:booger) { [] }
subject { {} }
subject(:wat) { {} }
subject!(:boo) { {} }
end
end

# good
describe MyClass do
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
```
#### with AllowSubject configuration

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

# bad
describe MyClass do
let(:foo) { [] }
let(:bar) { [] }
let!(:booger) { [] }
let(:subject) { {} }
let(:wat) { {} }
let!(:boo) { {} }
end

# good
describe MyClass do
let(:foo) { [] }
let(:bar) { [] }
let!(:booger) { [] }
subject { {} }
subject(:wat) { {} }
subject!(:boo) { {} }
end
```
#### with Max configuration

```ruby
# rubocop.yml
# RSpec/MemoizedHelpersInExampleGroup:
# Max: 0

# bad
describe MyClass do
let(:foo) { [] }
end

# good
describe MyClass do
def foo; []; 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

0 comments on commit 98ecb7e

Please sign in to comment.