Skip to content

Commit

Permalink
Add new Minitest/AssertInDelta and Minitest/RefuteInDelta cops
Browse files Browse the repository at this point in the history
  • Loading branch information
fatkodima committed Jul 2, 2020
1 parent 28dbd0c commit 3d27152
Show file tree
Hide file tree
Showing 10 changed files with 355 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### New features

* [#91](https://github.com/rubocop-hq/rubocop-minitest/pull/91): Add new `Minitest/AssertInDelta` and `Minitest/RefuteInDelta` cops. ([@fatkodima][])
* [#83](https://github.com/rubocop-hq/rubocop-minitest/pull/83): New cops `AssertPathExists` and `RefutePathExists` check for use of `assert_path_exists`/`refute_path_exists` instead of `assert(File.exist?(path))`/`refute(File.exist?(path))`. ([@fatkodima][])
* [#88](https://github.com/rubocop-hq/rubocop-minitest/pull/88): Add new `Minitest/MultipleAssertions` cop. ([@fatkodima][])
* [#87](https://github.com/rubocop-hq/rubocop-minitest/pull/87): Add new `Minitest/AssertSilent` cop. ([@fatkodima][])
Expand Down
12 changes: 12 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ Minitest/AssertEqual:
Enabled: true
VersionAdded: '0.4'

Minitest/AssertInDelta:
Description: 'This cop enforces the test to use `assert_in_delta` instead of using `assert_equal` to compare floats.'
StyleGuide: 'https://minitest.rubystyle.guide/#assert-in-delta'
Enabled: 'pending'
VersionAdded: '0.10'

Minitest/AssertMatch:
Description: 'This cop enforces the test to use `assert_match` instead of using `assert(matcher.match(object))`.'
StyleGuide: 'https://minitest.rubystyle.guide#assert-match'
Expand Down Expand Up @@ -107,6 +113,12 @@ Minitest/RefuteFalse:
Enabled: true
VersionAdded: '0.3'

Minitest/RefuteInDelta:
Description: 'This cop enforces the test to use `refute_in_delta` instead of using `refute_equal` to compare floats.'
StyleGuide: 'https://minitest.rubystyle.guide/#refute-in-delta'
Enabled: 'pending'
VersionAdded: '0.10'

Minitest/RefuteIncludes:
Description: 'This cop enforces the test to use `refute_includes` instead of using `refute(collection.include?(object))`.'
StyleGuide: 'https://minitest.rubystyle.guide#refute-includes'
Expand Down
2 changes: 2 additions & 0 deletions docs/modules/ROOT/pages/cops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* xref:cops_minitest.adoc#minitestassertempty[Minitest/AssertEmpty]
* xref:cops_minitest.adoc#minitestassertemptyliteral[Minitest/AssertEmptyLiteral]
* xref:cops_minitest.adoc#minitestassertequal[Minitest/AssertEqual]
* xref:cops_minitest.adoc#minitestassertindelta[Minitest/AssertInDelta]
* xref:cops_minitest.adoc#minitestassertincludes[Minitest/AssertIncludes]
* xref:cops_minitest.adoc#minitestassertinstanceof[Minitest/AssertInstanceOf]
* xref:cops_minitest.adoc#minitestassertkindof[Minitest/AssertKindOf]
Expand All @@ -19,6 +20,7 @@
* xref:cops_minitest.adoc#minitestrefuteempty[Minitest/RefuteEmpty]
* xref:cops_minitest.adoc#minitestrefuteequal[Minitest/RefuteEqual]
* xref:cops_minitest.adoc#minitestrefutefalse[Minitest/RefuteFalse]
* xref:cops_minitest.adoc#minitestrefuteindelta[Minitest/RefuteInDelta]
* xref:cops_minitest.adoc#minitestrefuteincludes[Minitest/RefuteIncludes]
* xref:cops_minitest.adoc#minitestrefuteinstanceof[Minitest/RefuteInstanceOf]
* xref:cops_minitest.adoc#minitestrefutekindof[Minitest/RefuteKindOf]
Expand Down
64 changes: 64 additions & 0 deletions docs/modules/ROOT/pages/cops_minitest.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,38 @@ assert_equal("rubocop-minitest", actual)

* https://minitest.rubystyle.guide#assert-equal-arguments-order

== Minitest/AssertInDelta

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

| Pending
| Yes
| Yes
| 0.10
| -
|===

This cop enforces the test to use `assert_in_delta`
instead of using `assert_equal` to compare floats.

=== Examples

[source,ruby]
----
# bad
assert_equal(0.2, actual)
assert_equal(0.2, actual, 'message')
# good
assert_in_delta(0.2, actual)
assert_in_delta(0.2, actual, 0.001, 'message')
----

=== References

* https://minitest.rubystyle.guide/#assert-in-delta

== Minitest/AssertIncludes

|===
Expand Down Expand Up @@ -560,6 +592,38 @@ refute(actual, 'message')

* https://minitest.rubystyle.guide#refute-false

== Minitest/RefuteInDelta

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

| Pending
| Yes
| Yes
| 0.10
| -
|===

This cop enforces the test to use `refute_in_delta`
instead of using `refute_equal` to compare floats.

=== Examples

[source,ruby]
----
# bad
refute_equal(0.2, actual)
refute_equal(0.2, actual, 'message')
# good
refute_in_delta(0.2, actual)
refute_in_delta(0.2, actual, 0.001, 'message')
----

=== References

* https://minitest.rubystyle.guide/#refute-in-delta

== Minitest/RefuteIncludes

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

module RuboCop
module Cop
module Minitest
# This cop enforces the test to use `assert_in_delta`
# instead of using `assert_equal` to compare floats.
#
# @example
# # bad
# assert_equal(0.2, actual)
# assert_equal(0.2, actual, 'message')
#
# # good
# assert_in_delta(0.2, actual)
# assert_in_delta(0.2, actual, 0.001, 'message')
#
class AssertInDelta < Cop
include InDeltaMixin

def_node_matcher :equal_floats_call, <<~PATTERN
(send nil? :assert_equal $_ $_ $...)
PATTERN
end
end
end
end
27 changes: 27 additions & 0 deletions lib/rubocop/cop/minitest/refute_in_delta.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Minitest
# This cop enforces the test to use `refute_in_delta`
# instead of using `refute_equal` to compare floats.
#
# @example
# # bad
# refute_equal(0.2, actual)
# refute_equal(0.2, actual, 'message')
#
# # good
# refute_in_delta(0.2, actual)
# refute_in_delta(0.2, actual, 0.001, 'message')
#
class RefuteInDelta < Cop
include InDeltaMixin

def_node_matcher :equal_floats_call, <<~PATTERN
(send nil? :refute_equal $_ $_ $...)
PATTERN
end
end
end
end
3 changes: 3 additions & 0 deletions lib/rubocop/cop/minitest_cops.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# frozen_string_literal: true

require_relative 'mixin/argument_range_helper'
require_relative 'mixin/in_delta_mixin'
require_relative 'mixin/minitest_cop_rule'
require_relative 'mixin/minitest_exploration_helpers'
require_relative 'minitest/assert_empty'
require_relative 'minitest/assert_empty_literal'
require_relative 'minitest/assert_equal'
require_relative 'minitest/assert_in_delta'
require_relative 'minitest/assert_kind_of'
require_relative 'minitest/assert_nil'
require_relative 'minitest/assert_includes'
Expand All @@ -20,6 +22,7 @@
require_relative 'minitest/refute_empty'
require_relative 'minitest/refute_false'
require_relative 'minitest/refute_equal'
require_relative 'minitest/refute_in_delta'
require_relative 'minitest/refute_kind_of'
require_relative 'minitest/refute_nil'
require_relative 'minitest/refute_includes'
Expand Down
55 changes: 55 additions & 0 deletions lib/rubocop/cop/mixin/in_delta_mixin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

module RuboCop
module Cop
# Common functionality for `AssertInDelta` and `RefuteInDelta` cops.
module InDeltaMixin
MSG = 'Prefer using `%<good_method>s` over `%<bad_method>s`.'

def on_send(node)
equal_floats_call(node) do |expected, actual, message|
message = message.first

if expected.float_type? || actual.float_type?
message = format(MSG,
good_method: build_good_method(expected, actual, message),
bad_method: build_bad_method(expected, actual, message))

add_offense(node, message: message)
end
end
end

def autocorrect(node)
equal_floats_call(node) do |expected, actual, message|
message = message.first
replacement = build_good_method(expected, actual, message)

lambda do |corrector|
corrector.replace(node, replacement)
end
end
end

private

def build_good_method(expected, actual, message)
if message
"#{assertion_method}_in_delta(#{expected.source}, #{actual.source}, 0.001, #{message.source})"
else
"#{assertion_method}_in_delta(#{expected.source}, #{actual.source})"
end
end

def build_bad_method(expected, actual, message)
arguments = [expected.source, actual.source, message&.source].compact.join(', ')
"#{assertion_method}_equal(#{arguments})"
end

def assertion_method
class_name = self.class.name.split('::').last
class_name[/\A[[:upper:]][[:lower:]]+/].downcase
end
end
end
end
82 changes: 82 additions & 0 deletions test/rubocop/cop/minitest/assert_in_delta_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# frozen_string_literal: true

require 'test_helper'

class AssertInDeltaTest < Minitest::Test
def test_registers_offense_when_using_assert_equal_with_float_as_expected_value
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_equal(0.2, foo)
^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_in_delta(0.2, foo)` over `assert_equal(0.2, foo)`.
end
end
RUBY

assert_correction(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_in_delta(0.2, foo)
end
end
RUBY
end

def test_registers_offense_when_using_assert_equal_with_float_as_actual_value
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_equal(foo, 0.2)
^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_in_delta(foo, 0.2)` over `assert_equal(foo, 0.2)`.
end
end
RUBY

assert_correction(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_in_delta(foo, 0.2)
end
end
RUBY
end

def test_registers_offense_when_using_assert_equal_with_float_and_message
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_equal(0.2, foo, 'message')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_in_delta(0.2, foo, 0.001, 'message')` over `assert_equal(0.2, foo, 'message')`.
end
end
RUBY

assert_correction(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_in_delta(0.2, foo, 0.001, 'message')
end
end
RUBY
end

def test_does_not_register_offense_when_using_assert_equal_with_floats
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_equal foo, bar
end
end
RUBY
end

def test_does_not_register_offense_when_using_assert_in_delta_method
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_in_delta(foo, 0.2)
end
end
RUBY
end
end

0 comments on commit 3d27152

Please sign in to comment.