Skip to content

Commit

Permalink
Add configuration option AllowNested to RSpec/MultipleExpectations
Browse files Browse the repository at this point in the history
Fix: #379
  • Loading branch information
ydah committed Feb 28, 2024
1 parent 0f70932 commit bd4b8d8
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -15,6 +15,7 @@
- Fix a false positive for `RSpec/ExpectActual` when used with rspec-rails routing matchers. ([@naveg])
- Add new `RSpec/RepeatedSubjectCall` cop. ([@drcapulet])
- Add configuration option `ResponseMethods` to `RSpec/Rails/HaveHttpStatus`. ([@ydah])
- Add configuration option `AllowNested` to `RSpec/MultipleExpectations`. ([@ydah])

## 2.26.1 (2024-01-05)

Expand Down
3 changes: 2 additions & 1 deletion config/default.yml
Expand Up @@ -651,8 +651,9 @@ RSpec/MultipleExpectations:
Description: Checks if examples contain too many `expect` calls.
Enabled: true
Max: 1
AllowNested: false
VersionAdded: '1.7'
VersionChanged: '1.21'
VersionChanged: "<<next>>"
StyleGuide: https://rspec.rubystyle.guide/#expectation-per-example
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleExpectations

Expand Down
34 changes: 33 additions & 1 deletion docs/modules/ROOT/pages/cops_rspec.adoc
Expand Up @@ -3415,7 +3415,7 @@ end
| Yes
| No
| 1.7
| 1.21
| <<next>>
|===
Checks if examples contain too many `expect` calls.
Expand Down Expand Up @@ -3490,6 +3490,34 @@ describe UserCreator do
end
----
==== `AllowNested: false` (default)
[source,ruby]
----
# bad
describe UserCreator do
it 'raises an error' do
expect { my_code }.to raise_error(MyErrorType) do |error|
expect(error.message).to match /something went wrong/
end
end
end
----
==== `AllowNested: true`
[source,ruby]
----
# good
describe UserCreator do
it 'raises an error' do
expect { my_code }.to raise_error(MyErrorType) do |error|
expect(error.message).to match /something went wrong/
end
end
end
----
=== Configurable attributes
|===
Expand All @@ -3498,6 +3526,10 @@ end
| Max
| `1`
| Integer
| AllowNested
| `false`
| Boolean
|===
=== References
Expand Down
26 changes: 26 additions & 0 deletions lib/rubocop/cop/rspec/multiple_expectations.rb
Expand Up @@ -61,6 +61,26 @@ module RSpec
# end
# end
#
# @example `AllowNested: false` (default)
# # bad
# describe UserCreator do
# it 'raises an error' do
# expect { my_code }.to raise_error(MyErrorType) do |error|
# expect(error.record.persisted?).to be(true)
# end
# end
# end
#
# @example `AllowNested: true`
# # good
# describe UserCreator do
# it 'raises an error' do
# expect { my_code }.to raise_error(MyErrorType) do |error|
# expect(error.record.persisted?).to be(true)
# end
# end
# end
#
class MultipleExpectations < Base
include ConfigurableMax

Expand Down Expand Up @@ -119,6 +139,8 @@ def find_expectation(node, &block)
# do not search inside of aggregate_failures block
return if aggregate_failures_block?(node)

return if node.block_type? && allow_nested?

node.each_child_node do |child|
find_expectation(child, &block)
end
Expand All @@ -138,6 +160,10 @@ def flag_example(node, expectation_count:)
def max_expectations
Integer(cop_config.fetch('Max', 1))
end

def allow_nested?
cop_config.fetch('AllowNested', false)
end
end
end
end
Expand Down
33 changes: 33 additions & 0 deletions spec/rubocop/cop/rspec/multiple_expectations_spec.rb
Expand Up @@ -238,6 +238,39 @@
end
end

context 'with `AllowNested: false`' do
let(:cop_config) { { 'AllowNested' => false } }

it 'flags nested expects' do
expect_offense(<<~RUBY)
describe Foo do
it 'uses expect with block twice' do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Example has too many expectations [2/1].
expect { my_code }.to raise_error(MyErrorType) do |error|
expect(error.record.persisted?).to be(true)
end
end
end
RUBY
end
end

context 'with `AllowNested: true`' do
let(:cop_config) { { 'AllowNested' => true } }

it 'ignores nested expects' do
expect_no_offenses(<<~RUBY)
describe Foo do
it 'uses expect with block twice' do
expect { my_code }.to raise_error(MyErrorType) do |error|
expect(error.record.persisted?).to be(true)
end
end
end
RUBY
end
end

it 'generates a todo based on the worst offense' do
inspect_source(<<~RUBY, 'spec/foo_spec.rb')
describe Foo do
Expand Down

0 comments on commit bd4b8d8

Please sign in to comment.