diff --git a/CHANGELOG.md b/CHANGELOG.md index ddb9c35e0..f56a5fe33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Fix `RSpec/FilePath` detection when absolute path includes test subject. ([@eitoball][]) * Add new `Capybara/VisibilityMatcher` cop. ([@aried3r][]) * Ignore String constants by `RSpec/Describe`. ([@AlexWayfer][]) +* Add new `RSpec/EmptyHook` cop. ([@tejasbubane][]) ## 1.38.1 (2020-02-15) @@ -496,3 +497,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@eitoball]: https://github.com/eitoball [@aried3r]: https://github.com/aried3r [@AlexWayfer]: https://github.com/AlexWayfer +[@tejasbubane]: https://github.com/tejasbubane diff --git a/config/default.yml b/config/default.yml index 2b1e87b16..b212884ef 100644 --- a/config/default.yml +++ b/config/default.yml @@ -105,6 +105,12 @@ RSpec/EmptyExampleGroup: CustomIncludeMethods: [] StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup +RSpec/EmptyHook: + Description: Checks for empty before and after hooks. + Enabled: true + VersionAdded: 1.38.2 + StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyHook + RSpec/EmptyLineAfterExample: Description: Checks if there is an empty line after example blocks. Enabled: true diff --git a/lib/rubocop/cop/rspec/empty_hook.rb b/lib/rubocop/cop/rspec/empty_hook.rb new file mode 100644 index 000000000..43c820b9c --- /dev/null +++ b/lib/rubocop/cop/rspec/empty_hook.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Checks for empty before and after hooks. + # + # @example + # # bad + # before {} + # after do; end + # before(:all) do + # end + # all(:all) { nil } + # + # # good + # before { create_users } + # after do; cleanup_users; end + # before(:all) do + # create_feed + # end + # all(:all) { cleanup_feed } + class EmptyHook < Cop + MSG = 'Empty hook detected.' + + def_node_matcher :empty_hook?, <<~PATTERN + (block + $(send nil? {:before :after} + (sym {:each :all :suite})?) + _ {(:nil) nil?}) + PATTERN + + def on_block(node) + empty_hook?(node) do |hook| + add_offense(hook) + end + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec_cops.rb b/lib/rubocop/cop/rspec_cops.rb index f9b2e5d36..0231d2060 100644 --- a/lib/rubocop/cop/rspec_cops.rb +++ b/lib/rubocop/cop/rspec_cops.rb @@ -30,6 +30,7 @@ require_relative 'rspec/described_class_module_wrapping' require_relative 'rspec/dialect' require_relative 'rspec/empty_example_group' +require_relative 'rspec/empty_hook' require_relative 'rspec/empty_line_after_example' require_relative 'rspec/empty_line_after_example_group' require_relative 'rspec/empty_line_after_final_let' diff --git a/manual/cops.md b/manual/cops.md index f20653afe..20ddb9617 100644 --- a/manual/cops.md +++ b/manual/cops.md @@ -29,6 +29,7 @@ * [RSpec/DescribedClassModuleWrapping](cops_rspec.md#rspecdescribedclassmodulewrapping) * [RSpec/Dialect](cops_rspec.md#rspecdialect) * [RSpec/EmptyExampleGroup](cops_rspec.md#rspecemptyexamplegroup) +* [RSpec/EmptyHook](cops_rspec.md#rspecemptyhook) * [RSpec/EmptyLineAfterExample](cops_rspec.md#rspecemptylineafterexample) * [RSpec/EmptyLineAfterExampleGroup](cops_rspec.md#rspecemptylineafterexamplegroup) * [RSpec/EmptyLineAfterFinalLet](cops_rspec.md#rspecemptylineafterfinallet) diff --git a/manual/cops_rspec.md b/manual/cops_rspec.md index 78f371f1c..2fc262832 100644 --- a/manual/cops_rspec.md +++ b/manual/cops_rspec.md @@ -639,6 +639,43 @@ CustomIncludeMethods | `[]` | Array * [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup) +## RSpec/EmptyHook + +Enabled by default | Supports autocorrection +--- | --- +Enabled | No + +Checks for empty before and after hooks. + +### Examples + +```ruby +# bad +before {} +after do; end +before(:all) do +end +all(:all) { nil } + +# good +before { create_users } +after do; cleanup_users; end +before(:all) do + create_feed +end +all(:all) { cleanup_feed } +``` + +### Configurable attributes + +Name | Default value | Configurable values +--- | --- | --- +VersionAdded | `1.38.2` | String + +### References + +* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyHook](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyHook) + ## RSpec/EmptyLineAfterExample Enabled by default | Supports autocorrection diff --git a/spec/rubocop/cop/rspec/empty_hook_spec.rb b/spec/rubocop/cop/rspec/empty_hook_spec.rb new file mode 100644 index 000000000..fc54f93d8 --- /dev/null +++ b/spec/rubocop/cop/rspec/empty_hook_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::RSpec::EmptyHook do + subject(:cop) { described_class.new(config) } + + let(:config) { RuboCop::Config.new } + + shared_examples 'empty hook' do |hook_type| + it 'registers offense with empty block' do + expect_offense(<<~RUBY) + #{hook_type} {} + #{'^' * hook_type.length} Empty hook detected. + RUBY + end + + it 'registers offense with empty single-line do-end' do + expect_offense(<<~RUBY) + #{hook_type} do; end + #{'^' * hook_type.length} Empty hook detected. + RUBY + end + + it 'registers offense with empty multi-line do-end' do + expect_offense(<<~RUBY) + #{hook_type} do + #{'^' * hook_type.length} Empty hook detected. + end + RUBY + end + + it 'registers offense with empty single-line nil' do + expect_offense(<<~RUBY) + #{hook_type} { nil } + #{'^' * hook_type.length} Empty hook detected. + RUBY + end + + it 'registers offense with empty multi-line nil' do + expect_offense(<<~RUBY) + #{hook_type} do + #{'^' * hook_type.length} Empty hook detected. + nil + end + RUBY + end + + it 'allows single-line non-empty blocks' do + expect_no_offenses(<<~RUBY) + #{hook_type} { create_users } + RUBY + end + + it 'allows multi-line non-empty blocks' do + expect_no_offenses(<<~RUBY) + #{hook_type} do + create_users + create_businesses + end + RUBY + end + end + + context 'with before hooks' do + it_behaves_like 'empty hook', 'before' + it_behaves_like 'empty hook', 'before(:each)' + it_behaves_like 'empty hook', 'before(:suite)' + it_behaves_like 'empty hook', 'before(:all)' + end + + context 'with after hooks' do + it_behaves_like 'empty hook', 'after' + it_behaves_like 'empty hook', 'after(:each)' + it_behaves_like 'empty hook', 'after(:suite)' + it_behaves_like 'empty hook', 'after(:all)' + end +end