Skip to content

Commit 2876efd

Browse files
pockebackus
authored andcommitted
Add new RSpec/ExpectInHook cop (#445)
1 parent 9d4b6bf commit 2876efd

File tree

6 files changed

+144
-0
lines changed

6 files changed

+144
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* Add `RSpec/InvalidPredicateMatcher` cop. ([@pocke][])
1313
* Change HookArgument cop to detect when hook has a receiver. ([@pocke][])
1414
* Add `RSpec/PredicateMatcher` cop. ([@pocke][])
15+
* Add `RSpec/ExpectInHook` cop. ([@pocke][])
1516

1617
## 1.15.1 (2017-04-30)
1718

config/default.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ RSpec/ExpectActual:
113113
- spec/routing/**/*
114114
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectActual
115115

116+
RSpec/ExpectInHook:
117+
Enabled: true
118+
Description: Do not use `expect` in hooks such as `before`.
119+
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectInHook
120+
116121
RSpec/ExpectOutput:
117122
Description: Checks for opportunities to use `expect { ... }.to output`.
118123
Enabled: true

lib/rubocop-rspec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
require 'rubocop/cop/rspec/example_length'
3939
require 'rubocop/cop/rspec/example_wording'
4040
require 'rubocop/cop/rspec/expect_actual'
41+
require 'rubocop/cop/rspec/expect_in_hook'
4142
require 'rubocop/cop/rspec/expect_output'
4243
require 'rubocop/cop/rspec/factory_girl/dynamic_attribute_defined_statically'
4344
require 'rubocop/cop/rspec/file_path'
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
# Do not use `expect` in hooks such as `before`.
7+
#
8+
# @example
9+
# # bad
10+
# before do
11+
# expect(something).to eq 'foo'
12+
# end
13+
#
14+
# # bad
15+
# after do
16+
# expect_any_instance_of(Something).to receive(:foo)
17+
# end
18+
#
19+
# # good
20+
# it do
21+
# expect(something).to eq 'foo'
22+
# end
23+
class ExpectInHook < Cop
24+
MSG = 'Do not use `%<expect>s` in `%<hook>s` hook'.freeze
25+
HOOKS = Hooks::ALL.node_pattern_union.freeze
26+
27+
def_node_matcher :hook, <<-PATTERN
28+
(block (send _ $#{HOOKS} ...) _ $!nil)
29+
PATTERN
30+
31+
def_node_search :expect, <<-PATTERN
32+
{
33+
#{Expectations::ALL.send_pattern}
34+
#{Expectations::ALL.block_pattern}
35+
}
36+
PATTERN
37+
38+
def on_block(node)
39+
hook(node) do |hook_name, body|
40+
expect(body) do |expect|
41+
method = send_node(expect)
42+
add_offense(method, :selector,
43+
message(method, hook_name))
44+
end
45+
end
46+
end
47+
48+
private
49+
50+
def message(expect, hook)
51+
format(MSG, expect: expect.method_name, hook: hook)
52+
end
53+
54+
def send_node(node)
55+
return node if node.send_type?
56+
node.children.first
57+
end
58+
end
59+
end
60+
end
61+
end

lib/rubocop/rspec/language.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ module Subject
106106
ALL = SelectorSet.new(%i[subject subject!])
107107
end
108108

109+
module Expectations
110+
ALL = SelectorSet.new(%i[expect expect_any_instance_of])
111+
end
112+
109113
ALL =
110114
ExampleGroups::ALL +
111115
SharedGroups::ALL +
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::ExpectInHook do
4+
subject(:cop) { described_class.new }
5+
6+
it 'adds an offense for `expect` in `before` hook' do
7+
expect_offense(<<-RUBY)
8+
before do
9+
expect(something).to eq('foo')
10+
^^^^^^ Do not use `expect` in `before` hook
11+
expect_any_instance_of(Something).to receive(:foo)
12+
^^^^^^^^^^^^^^^^^^^^^^ Do not use `expect_any_instance_of` in `before` hook
13+
end
14+
RUBY
15+
end
16+
17+
it 'adds an offense for `expect` in `after` hook' do
18+
expect_offense(<<-RUBY)
19+
after do
20+
expect(something).to eq('foo')
21+
^^^^^^ Do not use `expect` in `after` hook
22+
expect_any_instance_of(Something).to receive(:foo)
23+
^^^^^^^^^^^^^^^^^^^^^^ Do not use `expect_any_instance_of` in `after` hook
24+
end
25+
RUBY
26+
end
27+
28+
it 'adds an offense for `expect` in `around` hook' do
29+
expect_offense(<<-RUBY)
30+
around do
31+
expect(something).to eq('foo')
32+
^^^^^^ Do not use `expect` in `around` hook
33+
expect_any_instance_of(Something).to receive(:foo)
34+
^^^^^^^^^^^^^^^^^^^^^^ Do not use `expect_any_instance_of` in `around` hook
35+
end
36+
RUBY
37+
end
38+
39+
it 'adds an offense for `expect` with block in `before` hook' do
40+
expect_offense(<<-RUBY)
41+
before do
42+
expect { something }.to eq('foo')
43+
^^^^^^ Do not use `expect` in `before` hook
44+
end
45+
RUBY
46+
end
47+
48+
it 'accepts an empty `before` hook' do
49+
expect_no_offenses(<<-RUBY)
50+
before do
51+
end
52+
RUBY
53+
end
54+
55+
it 'accepts `allow` in `before` hook' do
56+
expect_no_offenses(<<-RUBY)
57+
before do
58+
allow(something).to receive(:foo)
59+
allow_any_instance_of(something).to receive(:foo)
60+
end
61+
RUBY
62+
end
63+
64+
it 'accepts `expect` in `it`' do
65+
expect_no_offenses(<<-RUBY)
66+
it do
67+
expect(something).to eq('foo')
68+
expect_any_instance_of(something).to receive(:foo)
69+
end
70+
RUBY
71+
end
72+
end

0 commit comments

Comments
 (0)