Skip to content

Commit

Permalink
Added a cop to enforce assert_includes over assert(collection.include…
Browse files Browse the repository at this point in the history
…s?(actual))
  • Loading branch information
abhaynikam committed Sep 4, 2019
1 parent bea0298 commit ea42ea1
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 0 deletions.
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ Minitest/AssertNil:
StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-nil'
Enabled: true
VersionAdded: '0.1'

Minitest/AssertIncludes:
Description: 'Check if your test uses `assert_includes` instead of `assert(collection.includes?(actual))`.'
StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#assert-includes'
Enabled: true
VersionAdded: '0.2'
60 changes: 60 additions & 0 deletions lib/rubocop/cop/minitest/assert_includes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Minitest
# Check if your test uses `assert_includes`
# instead of `assert(collection.includes?(actual))`.
#
# @example
# # bad
# assert(collection.includes?(actual))
# assert(collection.includes?(actual), 'the message')
#
# # good
# assert_includes(collection, actual)
# assert_includes(collection, actual, 'the message')
#
class AssertIncludes < Cop
MSG = 'Prefer using `assert_includes(%<arguments>s)` over ' \
'`assert(%<receiver>s)`.'

def_node_matcher :assert_with_includes, <<~PATTERN
(send nil? :assert $(send $_ :includes? $_) $...)
PATTERN

def on_send(node)
assert_with_includes(node) do
|first_receiver_arg, collection, actual, rest_receiver_arg|

message = rest_receiver_arg.first
arguments = node_arguments(collection, actual, message)
receiver = [first_receiver_arg.source, message&.source].compact.join(', ')

offense_message = format(MSG, arguments: arguments, receiver: receiver)

add_offense(node, message: offense_message)
end
end

def autocorrect(node)
lambda do |corrector|
assert_with_includes(node) do
|_receiver, collection, actual, rest_receiver_arg|

message = rest_receiver_arg.first
replacement = node_arguments(collection, actual, message)
corrector.replace(node.loc.expression, "assert_includes(#{replacement})")
end
end
end

private

def node_arguments(collection, actual, message)
[collection.source, actual.source, message&.source].compact.join(', ')
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
@@ -1,3 +1,4 @@
# frozen_string_literal: true

require_relative 'minitest/assert_nil'
require_relative 'minitest/assert_includes'
1 change: 1 addition & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<!-- START_COP_LIST -->
#### Department [Minitest](cops_minitest.md)

* [Minitest/AssertIncludes](cops_minitest.md#minitestassertincludes)
* [Minitest/AssertNil](cops_minitest.md#minitestassertnil)

<!-- END_COP_LIST -->
25 changes: 25 additions & 0 deletions manual/cops_minitest.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Minitest

## Minitest/AssertIncludes

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Enabled | Yes | Yes | 0.2 | -

Check if your test uses `assert_includes`
instead of `assert(collection.includes?(actual))`.

### Examples

```ruby
# bad
assert(collection.includes?(actual))
assert(collection.includes?(actual), 'the message')

# good
assert_includes(collection, actual)
assert_includes(collection, actual, 'the message')
```

### References

* [https://github.com/rubocop-hq/minitest-style-guide#assert-includes](https://github.com/rubocop-hq/minitest-style-guide#assert-includes)

## Minitest/AssertNil

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
101 changes: 101 additions & 0 deletions test/rubocop/cop/minitest/assert_includes_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# frozen_string_literal: true

require 'test_helper'

class AssertIncludesTest < Minitest::Test
def setup
@cop = RuboCop::Cop::Minitest::AssertIncludes.new
end

def test_adds_offense_for_use_of_assert_collection_includes
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert(collection.includes?(actual))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_includes(collection, actual)` over `assert(collection.includes?(actual))`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert_includes(collection, actual)
end
end
RUBY
end

def test_adds_offense_for_use_of_assert_collection_includes_with_message
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert(collection.includes?(actual), 'the message')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_includes(collection, actual, 'the message')` over `assert(collection.includes?(actual), 'the message')`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert_includes(collection, actual, 'the message')
end
end
RUBY
end

def test_adds_offense_for_use_of_assert_collection_includes_with_variable_in_message
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
message = 'the message'
assert(collection.includes?(actual), message)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_includes(collection, actual, message)` over `assert(collection.includes?(actual), message)`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
message = 'the message'
assert_includes(collection, actual, message)
end
end
RUBY
end

def test_adds_offense_for_use_of_assert_collection_includes_with_constant_in_message
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
MESSAGE = 'the message'
def test_do_something
assert(collection.includes?(actual), MESSAGE)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_includes(collection, actual, MESSAGE)` over `assert(collection.includes?(actual), MESSAGE)`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
MESSAGE = 'the message'
def test_do_something
assert_includes(collection, actual, MESSAGE)
end
end
RUBY
end

def test_does_not_offend_if_using_assert_nil
assert_no_offenses(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert_includes(collection, actual)
end
end
RUBY
end
end

0 comments on commit ea42ea1

Please sign in to comment.