Class: RuboCop::Cop::DevDoc::Test::AvoidUnitTest

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/dev_doc/test/avoid_unit_test.rb

Overview

Prefer controller tests; flag unit/service tests (‘< ActiveSupport::TestCase`).

## Rationale What the user sees and experiences is what matters; internal implementation does not. A controller test exercises behaviour end-to-end through the same path a user takes, so it catches the regressions that actually reach production. A unit/service test is only needed when a code path genuinely cannot be reached through a controller test (very rare) — e.g. a search-ranking detail the controller never exposes.

This cop flags only the literal ‘< ActiveSupport::TestCase` superclass. The blessed blackbox bases —`ActionDispatch::IntegrationTest`, `Glib::IntegrationTest`, `ActionMailer::TestCase`, `ActiveJob::TestCase` — are NOT flagged, even though they inherit from `ActiveSupport::TestCase` transitively.

## Escape hatch When a unit test is genuinely necessary, suppress with a reason that explains why a controller test can’t cover the path. That reason IS the required justification — keep it specific and reviewable:

# rubocop:disable DevDoc/Test/AvoidUnitTest -- search ranking isn't visible through the controller
class Ai::Retrieval::PgSearchStrategyTest < ActiveSupport::TestCase
  # ...
end
# rubocop:enable DevDoc/Test/AvoidUnitTest

NOTE: The cop matches the direct superclass only. A project base (‘class ApplicationServiceTest < ActiveSupport::TestCase`) is flagged once (justify it there); subclasses of that base are not re-flagged.

Examples:

# bad
class NoteRenderingTest < ActiveSupport::TestCase
end

# good — exercised through the controller
class NotesControllerTest < ActionDispatch::IntegrationTest
end

# good — genuinely unit-only, justified with a reason
# rubocop:disable DevDoc/Test/AvoidUnitTest -- <why a controller test can't cover this>
class SomeServiceTest < ActiveSupport::TestCase
end
# rubocop:enable DevDoc/Test/AvoidUnitTest

Constant Summary collapse

MSG =
'Prefer a controller test — unit tests are a rare exception. If a controller test ' \
'genuinely cannot cover this path, disable this cop on the class with a reason.'.freeze

Instance Method Summary collapse

Instance Method Details

#on_class(node) ⇒ Object



55
56
57
58
59
60
61
# File 'lib/rubocop/cop/dev_doc/test/avoid_unit_test.rb', line 55

def on_class(node)
  superclass = node.parent_class
  return unless superclass&.const_type?
  return unless superclass.const_name == 'ActiveSupport::TestCase'

  add_offense(superclass)
end