Class: RuboCop::Cop::DevDoc::Test::ResponseAssertEqual
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Test::ResponseAssertEqual
- Defined in:
- lib/rubocop/cop/dev_doc/test/response_assert_equal.rb
Overview
Controller tests that assert on the rendered response.body should
also snapshot the full response with response_assert_equal.
Rationale
response_assert_equal (glib-web's Glib::TestHelpers) is the most
blackbox assertion available — it snapshots the entire rendered
response, so it catches regressions anywhere in the output, not just
the one substring a targeted assertion happens to check. Per the
testing best practice, happy- and unhappy-path controller tests
should always use it.
The common slip this cop guards against: a test inspects the rendered
body with assert_match / assert_no_match / assert_includes /
response.body.scan(...) but forgets the snapshot. The targeted
assertion documents intent, but on its own it only proves the one
line — collateral changes elsewhere in the response slip through.
❌ Inspects the body, but no snapshot
test 'index lists the item' do
get items_url(format: :json)
assert_response :success
assert_match 'Widget', response.body
end
✔️ Keep the focused assertion AND snapshot the whole response
test 'index lists the item' do
get items_url(format: :json)
assert_response :success
assert_match 'Widget', response.body
response_assert_equal
end
What is NOT flagged
- Tests that already call
response_assert_equal. - Exception/redirect paths — a test asserting a non-success status
(
assert_response :not_found,:forbidden,:redirect, etc.) often has no stable rendered body to snapshot. - State-change tests that never read
response.body(e.g.assert_difference 'Model.count', mailer assertions). Passingresponse.bodyto a helper likesubmit_form(response.body, ...)is not a body assertion and is not flagged.
Inline disable
For the rare happy-path test whose response is genuinely
non-deterministic and cannot be made stable, add a
rubocop:disable DevDoc/Test/ResponseAssertEqual comment to the
test '...' do line, with a written reason on the next line
(e.g. "chunked response includes a per-run boundary token").
Constant Summary collapse
- MSG =
'Asserts on `response.body` but never calls `response_assert_equal`. ' \ 'Add the snapshot assertion (usually last) — it captures the whole rendered ' \ 'response, a stronger guard than a targeted body assertion. Exception-path ' \ 'tests that cannot snapshot may disable this cop with a reason.'.freeze
- NON_SUCCESS_STATUS_SYMBOLS =
Non-success HTTP statuses (symbol form). A test asserting any of these is an exception / redirect / empty-body path — no JSON body to snapshot.
%i[ not_found forbidden unauthorized unprocessable_entity unprocessable_content bad_request conflict gone no_content not_modified redirect moved_permanently found see_other payment_required too_many_requests precondition_failed ].freeze
Instance Method Summary collapse
- #calls_snapshot?(node) ⇒ Object
- #on_block(node) ⇒ Object
- #response_body_read?(node) ⇒ Object
- #response_status?(node) ⇒ Object
- #test_block?(node) ⇒ Object
Instance Method Details
#calls_snapshot?(node) ⇒ Object
84 85 86 |
# File 'lib/rubocop/cop/dev_doc/test/response_assert_equal.rb', line 84 def_node_search :calls_snapshot?, <<~PATTERN (send nil? :response_assert_equal) PATTERN |
#on_block(node) ⇒ Object
100 101 102 103 104 105 106 107 108 |
# File 'lib/rubocop/cop/dev_doc/test/response_assert_equal.rb', line 100 def on_block(node) return unless test_block?(node) return unless node.body return if calls_snapshot?(node) return if exempt_from_snapshot?(node) return unless inspects_response_body?(node) add_offense(node.send_node.loc.selector) end |
#response_body_read?(node) ⇒ Object
79 80 81 |
# File 'lib/rubocop/cop/dev_doc/test/response_assert_equal.rb', line 79 def_node_matcher :response_body_read?, <<~PATTERN (send (send nil? :response) {:body :parsed_body}) PATTERN |
#response_status?(node) ⇒ Object
89 |
# File 'lib/rubocop/cop/dev_doc/test/response_assert_equal.rb', line 89 def_node_matcher :response_status?, '(send (send nil? :response) :status)' |
#test_block?(node) ⇒ Object
74 75 76 |
# File 'lib/rubocop/cop/dev_doc/test/response_assert_equal.rb', line 74 def_node_matcher :test_block?, <<~PATTERN (block (send nil? :test ...) _args _body) PATTERN |