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 e909801
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 6 deletions.
31 changes: 25 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,27 @@ 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, &block)
return to_enum(:child_block_until_group, node) unless block
return unless node.body

node_to_process = node.body.begin_type? ? node.body : node

node_to_process.each_child_node(:block)
.reject(&method(:example_or_shared_group_or_including?))
.each do |child|
yield child
child_block_until_group(child, &block)
end
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 and not referenced in shared example including' do
expect_offense(<<-RUBY)
describe Foo do
it_behaves_like 'bar' do
let(:a) { b }
let!(:baz) { foobar }
^^^^^^^^^^ Do not use `let!` to setup objects not referenced in tests.
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 e909801

Please sign in to comment.