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

[Fix #279] Add new Minitest/NonExecutableTestMethod cop #280

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ Minitest/NoTestCases:
Enabled: false
VersionAdded: '0.30'

Minitest/NonExecutableTestMethod:
Description: 'Checks uses of test methods outside test class.'
Enabled: pending
Severity: warning
VersionAdded: '<<next>>'

Minitest/NonPublicTestMethod:
Description: 'Detects non `public` (marked as `private` or `protected`) test methods.'
Enabled: pending
Expand Down
51 changes: 51 additions & 0 deletions lib/rubocop/cop/minitest/non_executable_test_method.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Minitest
# Checks for the use of test methods outside of a test class.
#
# Test methods should be defined within a test class to ensure their execution.
#
# NOTE: This cop assumes that classes whose superclass name includes the word
# "`Test`" are test classes, in order to prevent false positives.
#
# @example
#
# # bad
# class FooTest < Minitest::Test
# end
# def test_method_should_be_inside_test_class
# end
#
# # good
# class FooTest < Minitest::Test
# def test_method_should_be_inside_test_class
# end
# end
#
class NonExecutableTestMethod < Base
include MinitestExplorationHelpers

MSG = 'Test method should be defined inside a test class to ensure execution.'

def on_def(node)
return if !test_method?(node) || !use_test_class?
return if node.left_siblings.none? { |sibling| possible_test_class?(sibling) }

add_offense(node)
end

def use_test_class?
root_node = processed_source.ast

root_node.each_descendant(:class).any? { |class_node| test_class?(class_node) }
end

def possible_test_class?(node)
node.is_a?(AST::ClassNode) && test_class?(node) && node.parent_class.source.include?('Test')
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 @@ -30,6 +30,7 @@
require_relative 'minitest/assert_truthy'
require_relative 'minitest/duplicate_test_run'
require_relative 'minitest/empty_line_before_assertion_methods'
require_relative 'minitest/non_executable_test_method'
require_relative 'minitest/redundant_message_argument'
require_relative 'minitest/return_in_test_method'
require_relative 'minitest/test_file_name'
Expand Down
158 changes: 158 additions & 0 deletions test/rubocop/cop/minitest/non_executable_test_method_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# frozen_string_literal: true

require_relative '../../../test_helper'

class NonExecutableTestMethodTest < Minitest::Test
def test_registers_offense_when_test_method_is_defined_outside_minitest_test_class
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
end
def test_foo
^^^^^^^^^^^^ Test method should be defined inside a test class to ensure execution.
end
RUBY
end

def test_registers_offense_when_test_method_is_defined_outside_active_support_test_case_class
assert_offense(<<~RUBY)
class FooTest < ActiveSupport::TestCase
end

def test_foo
^^^^^^^^^^^^ Test method should be defined inside a test class to ensure execution.
end
RUBY
end

def test_does_not_register_offense_when_test_method_is_defined_outside_namespaced_test_class
assert_offense(<<~RUBY)
module M
class FooTest < Minitest::Test
def test_foo
end
end

def test_bar
^^^^^^^^^^^^ Test method should be defined inside a test class to ensure execution.
end
end
RUBY
end

def test_does_not_register_offense_when_non_test_method_is_defined_outside_test_class
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
end
def do_something
end
RUBY
end

def test_does_not_register_offense_when_non_test_method_is_defined_outside_active_support_test_case_class
assert_no_offenses(<<~RUBY)
class FooTest < ActiveSupport::TestCase
end
def do_something
end
RUBY
end

def test_does_not_register_offense_when_test_method_is_defined_inside_test_class
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_foo
end
end
RUBY
end

def test_does_not_register_offense_when_test_method_is_defined_inside_test_helper_class
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_foo
end
end

class TestHelperMailer < ActionMailer::Base
def test_parameter_args
end
end
RUBY
end

def test_does_not_register_offense_when_test_method_is_defined_inside_test_helper_module
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_foo
end
end

module TestHelper
def test_parameter_args
end
end
RUBY
end

def test_does_not_register_offense_when_test_class_in_which_test_method_is_defined_is_repeated
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_foo
end
end

class FooTest < Minitest::Test
def test_foo
end
end
RUBY
end

def test_does_not_register_offense_when_test_method_is_defined_and_test_class_is_not_defined
assert_no_offenses(<<~RUBY)
def test_something
end
RUBY
end

def test_does_not_register_offense_when_test_method_is_defined_inside_condition
assert_no_offenses(<<~RUBY)
module ActiveRecord
class AdapterTest < ActiveRecord::TestCase
unless current_adapter?(:PostgreSQLAdapter)
def test_update_prepared_statement
end
end
end
end
RUBY
end

def test_does_not_register_offense_when_nested_test_method_and_two_test_classes_are_defined
assert_no_offenses(<<~RUBY)
class FooTest < ActiveRecord::TestCase
def test_foo
def test_bar
end
end
end

class BarTest < ActiveRecord::TestCase
end
RUBY
end

def test_does_not_register_offense_when_non_test_case_class_is_defined_before_test_method
assert_no_offenses(<<~RUBY)
class SetTest < ActiveRecord::AbstractMysqlTestCase
class SetTest < ActiveRecord::Base
end

def test_should_not_be_unsigned
column = SetTest.columns_hash["set_column"]
assert_not_predicate column, :unsigned?
end
end
RUBY
end
end