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). Passing `response.body` to a helper like `submit_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 |