Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NoAssertions cop #124

Merged
merged 1 commit into from
Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## master (unreleased)

ghiculescu marked this conversation as resolved.
Show resolved Hide resolved
### New features

* [#124](https://github.com/rubocop/rubocop-minitest/pull/124): Add new `Minitest/NoAssertions` cop. ([@ghiculescu][])

### Changes

* [#129](https://github.com/rubocop/rubocop-minitest/pull/129): Drop Ruby 2.4 support. ([@koic][])
Expand Down
5 changes: 5 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ Minitest/MultipleAssertions:
VersionAdded: '0.10'
Max: 3

Minitest/NoAssertions:
Description: 'This cop checks for at least one assertion (or flunk) in tests.'
Enabled: false
VersionAdded: '0.12'

Minitest/RefuteEmpty:
Description: 'This cop enforces to use `refute_empty` instead of using `refute(object.empty?)`.'
StyleGuide: 'https://minitest.rubystyle.guide#refute-empty'
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ based on the https://minitest.rubystyle.guide/[Minitest Style Guide].
* xref:cops_minitest.adoc#minitestglobalexpectations[Minitest/GlobalExpectations]
* xref:cops_minitest.adoc#minitestliteralasactualargument[Minitest/LiteralAsActualArgument]
* xref:cops_minitest.adoc#minitestmultipleassertions[Minitest/MultipleAssertions]
* xref:cops_minitest.adoc#minitestnoassertions[Minitest/NoAssertions]
* xref:cops_minitest.adoc#minitestrefuteempty[Minitest/RefuteEmpty]
* xref:cops_minitest.adoc#minitestrefuteequal[Minitest/RefuteEqual]
* xref:cops_minitest.adoc#minitestrefutefalse[Minitest/RefuteFalse]
Expand Down
32 changes: 32 additions & 0 deletions docs/modules/ROOT/pages/cops_minitest.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,38 @@ end
| Integer
|===

== Minitest/NoAssertions

|===
| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged

| Disabled
| Yes
| No
| 0.12
| -
|===

This cop checks if test cases contain any assertion calls.

=== Examples

[source,ruby]
----
# bad
class FooTest < Minitest::Test
def test_the_truth
end
end

# good
class FooTest < Minitest::Test
def test_the_truth
assert true
end
end
----

== Minitest/RefuteEmpty

|===
Expand Down
48 changes: 48 additions & 0 deletions lib/rubocop/cop/minitest/no_assertions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Minitest
# This cop checks if test cases contain any assertion calls.
#
# @example
# # bad
# class FooTest < Minitest::Test
# def test_the_truth
# end
# end
#
# # good
# class FooTest < Minitest::Test
# def test_the_truth
# assert true
# end
# end
#
class NoAssertions < Base
include MinitestExplorationHelpers

MSG = 'Test case has no assertions.'

def on_class(class_node)
return unless test_class?(class_node)

test_cases(class_node).each do |node|
assertions_count = assertions_count(node)

next if assertions_count.positive?

add_offense(node.block_type? ? node.loc.expression : node.loc.name)
end
end

private

def assertions_count(node)
base = assertion?(node) ? 1 : 0
base + node.each_child_node.sum { |c| assertions_count(c) }
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/minitest_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
require_relative 'minitest/global_expectations'
require_relative 'minitest/literal_as_actual_argument'
require_relative 'minitest/multiple_assertions'
require_relative 'minitest/no_assertions'
require_relative 'minitest/refute_empty'
require_relative 'minitest/refute_false'
require_relative 'minitest/refute_equal'
Expand Down
18 changes: 14 additions & 4 deletions lib/rubocop/cop/mixin/minitest_exploration_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ module MinitestExplorationHelpers
refute_respond_to refute_same
].freeze

FLUNK = 'flunk'

LIFECYCLE_HOOK_METHODS = %i[
before_setup
setup
Expand All @@ -42,8 +44,8 @@ def test_case?(node)
end

def test_cases(class_node)
class_def_nodes(class_node)
.select { |def_node| test_case_name?(def_node.method_name) }
(class_def_nodes(class_node).select { |def_node| test_case_name?(def_node.method_name) }) +
test_method_calls(class_node)
end

def lifecycle_hooks(class_node)
Expand All @@ -62,6 +64,11 @@ def class_def_nodes(class_node)
end
end

# support https://api.rubyonrails.org/classes/ActiveSupport/Testing/Declarative.html
def test_method_calls(class_node)
class_node.each_child_node(:block).select { |blk| blk.each_child_node(:send).first&.method_name.to_s == 'test' }
end

def test_case_name?(name)
name.to_s.start_with?('test_')
end
Expand All @@ -82,11 +89,14 @@ def assertions(def_node)

def assertion?(node)
node.send_type? &&
ASSERTION_PREFIXES.any? { |prefix| node.method_name.to_s.start_with?(prefix) }
ASSERTION_PREFIXES.any? do |prefix|
method_name = node.method_name.to_s
method_name == FLUNK || method_name.start_with?(prefix)
end
end

def assertion_method?(method_name)
ASSERTION_METHODS.include?(method_name)
method_name == FLUNK || ASSERTION_METHODS.include?(method_name)
end

def lifecycle_hook_method?(node)
Expand Down
65 changes: 65 additions & 0 deletions test/rubocop/cop/minitest/no_assertions_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

require 'test_helper'

class NoAssertionsTest < Minitest::Test
def test_registers_offense_when_no_assertions
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_the_truth
^^^^^^^^^^^^^^ Test case has no assertions.
end
end
RUBY
end

def test_registers_offense_when_no_assertions_in_block_form
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
test "the truth" do
^^^^^^^^^^^^^^^^^^^ Test case has no assertions.
end
end
RUBY
end

def test_register_no_offense_if_test_has_assertion
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_the_truth
assert true
end
end
RUBY
end

def test_register_no_offense_for_unrelated_methods
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def foo_bar
puts "this isn't a test"
end
end
RUBY
end

def test_register_no_offense_for_unrelated_blocks
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
foo "bar" do
puts "this isn't a test"
end
end
RUBY
end

def test_register_no_offense_if_test_flunks
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_the_truth
flunk
end
end
RUBY
end
end