Class: RuboCop::Cop::DevDoc::Test::RequireGlibTravel
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Test::RequireGlibTravel
- Defined in:
- lib/rubocop/cop/dev_doc/test/require_glib_travel.rb
Overview
Controller tests must run inside a glib_travel block.
Rationale
Per the testing best practice ("Time Travel"), tests should always run
inside a glib_travel block — even when they do not appear
time-sensitive. Controller tests integrate HTTP, the database, and the
clock (timestamps, expiry windows, token validity, rate limits), so they
are the highest-leverage place to enforce it. A controller test that
does not freeze time passes today and flakes or regresses the moment an
ordering, expiry, or TTL boundary shifts under it.
The block form auto-returns to real time at the end of the test, so no teardown is needed:
test 'shows the row' do
glib_travel(FROZEN_TIME) do
get items_url(format: :json)
assert_response :success
end
end
This cop is a presence check — it only asks "did you freeze time?".
Whether you used the block form (vs. the leak-prone bare anchor) is
enforced separately by DevDoc/Test/RequireGlibTravelBlock, and
glib_travel_freeze vs. glib_travel by AvoidGlibTravelFreeze.
What is NOT flagged
- Tests that already call
glib_travelorglib_travel_freeze. - Non-controller test files — the cop is scoped to
test/controllers/**viaInclude, where the time-sensitivity risk is highest. (The best practice recommends the block form for every test; this cop enforces the subset with the strongest evidence.)
Inline disable
For the rare controller test that is genuinely time-independent and the
block adds noise, add a rubocop:disable DevDoc/Test/RequireGlibTravel
comment to the test's opening line (test '...' do or def test_*)
with a written reason.
Constant Summary collapse
- MSG =
'Wrap this test in a `glib_travel` block — controller tests integrate ' \ 'HTTP, DB, and time, so the frozen time must be deterministic. See the ' \ 'testing best practice ("Time Travel"). Disable with a written reason ' \ 'only if the test is genuinely time-independent.'.freeze
Instance Method Summary collapse
-
#calls_glib_time_helper?(node) ⇒ Object
Any glib time helper satisfies the presence check.
- #on_block(node) ⇒ Object
-
#on_def(node) ⇒ Object
Minitest also allows method-style tests (
def test_shows_the_row); those are the same kind of controller test and must freeze time too. - #test_block?(node) ⇒ Object
Instance Method Details
#calls_glib_time_helper?(node) ⇒ Object
Any glib time helper satisfies the presence check. glib_travel_freeze
counts as "froze time" here — its form is policed by
AvoidGlibTravelFreeze, not by this cop.
80 81 82 |
# File 'lib/rubocop/cop/dev_doc/test/require_glib_travel.rb', line 80 def_node_search :calls_glib_time_helper?, <<~PATTERN (send nil? {:glib_travel :glib_travel_freeze} ...) PATTERN |
#on_block(node) ⇒ Object
84 85 86 87 88 |
# File 'lib/rubocop/cop/dev_doc/test/require_glib_travel.rb', line 84 def on_block(node) return unless test_block?(node) check_test(node, node.send_node.loc.selector) end |
#on_def(node) ⇒ Object
Minitest also allows method-style tests (def test_shows_the_row);
those are the same kind of controller test and must freeze time too.
92 93 94 95 96 |
# File 'lib/rubocop/cop/dev_doc/test/require_glib_travel.rb', line 92 def on_def(node) return unless test_method?(node) check_test(node, node.loc.name) end |
#test_block?(node) ⇒ Object
72 73 74 |
# File 'lib/rubocop/cop/dev_doc/test/require_glib_travel.rb', line 72 def_node_matcher :test_block?, <<~PATTERN (block (send nil? :test ...) _args _body) PATTERN |