Skip to content

Commit

Permalink
RSpec/NestedGroups. Handle shared example groups and edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
andrykonchin committed Jun 29, 2020
1 parent af294ae commit 9072de8
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 6 deletions.
33 changes: 27 additions & 6 deletions lib/rubocop/cop/rspec/let_setup.rb
Expand Up @@ -28,14 +28,20 @@ module RSpec
class LetSetup < Cop
MSG = 'Do not use `let!` to setup objects not referenced in tests.'

def_node_matcher :example_or_shared_group_or_including?,
(
ExampleGroups::ALL + SharedGroups::ALL +
Includes::ALL
).block_pattern

def_node_matcher :let_bang, <<-PATTERN
(block $(send nil? :let! (sym $_)) args ...)
PATTERN

def_node_search :method_called?, '(send nil? %)'

def on_block(node)
return unless example_group?(node)
return unless example_or_shared_group_or_including?(node)

unused_let_bang(node) do |let|
add_offense(let)
Expand All @@ -50,14 +56,29 @@ def unused_let_bang(node)
end
end

def child_let_bang(block_node)
return unless block_node.body&.begin_type?
def child_let_bang(node)
enum = child_block_until_group(node).map(&method(:let_bang))

block_node.body.each_child_node(:block) do |child|
node_with_name = let_bang(child)
yield node_with_name if node_with_name
enum.compact.each do |node_with_name|
yield node_with_name
end
end

def child_block_until_group(node, &blk)
return to_enum(:child_block_until_group, node) unless blk
return unless node.body

unwrap_nested_begin(node).each_child_node(:block)
.reject(&method(:example_or_shared_group_or_including?))
.each do |child|
yield child
child_block_until_group(child, &blk)
end
end

def unwrap_nested_begin(node)
node.body.begin_type? ? node.body : node
end
end
end
end
Expand Down
49 changes: 49 additions & 0 deletions spec/rubocop/cop/rspec/let_setup_spec.rb
Expand Up @@ -63,4 +63,53 @@
end
RUBY
end

it 'complains when let! is used and not referenced in shared example group' do
expect_offense(<<-RUBY)
shared_context 'foo' do
let!(:bar) { baz }
^^^^^^^^^^ Do not use `let!` to setup objects not referenced in tests.
it 'does not use bar' do
expect(baz).to eq(qux)
end
end
RUBY
end

it 'complains when let! used in shared example including' do
expect_offense(<<-RUBY)
describe Foo do
it_behaves_like 'bar' do
let!(:baz) { foobar }
^^^^^^^^^^ Do not use `let!` to setup objects not referenced in tests.
let(:a) { b }
end
end
RUBY
end

it 'complains when there is only one nested node into example group' do
expect_offense(<<-RUBY)
describe Foo do
let!(:bar) { baz }
^^^^^^^^^^ Do not use `let!` to setup objects not referenced in tests.
end
RUBY
end

it 'complains when there is a custom nesting level' do
expect_offense(<<-RUBY)
describe Foo do
[].each do |i|
let!(:bar) { i }
^^^^^^^^^^ Do not use `let!` to setup objects not referenced in tests.
it 'does not use bar' do
expect(baz).to eq(qux)
end
end
end
RUBY
end
end

0 comments on commit 9072de8

Please sign in to comment.