diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 8cd6992ccb477..1db518052e6b7 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -623,12 +623,12 @@ def method_missing(method, ...) # Calling TestResponse#parsed_body on the response parses the response body # based on the last response MIME type. # - # Out of the box, only `:json` is supported. But for any custom MIME types - # you've registered, you can add your own encoders with: + # Out of the box, only `:html` and `:json` are supported. But for any custom MIME + # types you've registered, you can add your own encoders with: # # ActionDispatch::IntegrationTest.register_encoder :wibble, # param_encoder: -> params { params.to_wibble }, - # response_parser: -> body { body } + # response_parser: -> body { Wibble.parse(body) } # # Where `param_encoder` defines how the params should be encoded and # `response_parser` defines how the response body should be parsed through @@ -675,6 +675,17 @@ def app=(app) @@app = app end + # Registers an encoder used to parse the response body returned by + # TestResponse#parsed_body. + # + # Out of the box, only `:html` and `:json` are supported. For additional + # registered MIME types, register encoders with a `:param_encoder` option + # to encode a `params` argument and a `:response_parser` option to parsed + # the value of TestResponse#parsed_body: + # + # ActionDispatch::IntegrationTest.register_encoder :wibble, + # param_encoder: -> params { params.to_wibble }, + # response_parser: -> body { Wibble.parse(body) } def register_encoder(*args, **options) RequestEncoder.register_encoder(*args, **options) end diff --git a/guides/source/testing.md b/guides/source/testing.md index f223214b43e95..1c8a3c48155f4 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -1130,7 +1130,7 @@ In addition to the standard testing helpers, inheriting from `ActionDispatch::In For dealing with the integration test runner, see [`ActionDispatch::Integration::Runner`](https://api.rubyonrails.org/classes/ActionDispatch/Integration/Runner.html). -When performing requests, we will have [`ActionDispatch::Integration::RequestHelpers`](https://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html) available for our use. +When performing requests, we will have [`ActionDispatch::Integration::RequestHelpers`](https://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html) available for our use. Responses will be instances of [`ActionDispatch::TestResponse`](https://api.rubyonrails.org/classes/ActionDispatch/TestResponse.html). If we need to upload files, take a look at [`ActionDispatch::TestProcess::FixtureFile`](https://api.rubyonrails.org/classes/ActionDispatch/TestProcess/FixtureFile.html) to help. @@ -1340,6 +1340,35 @@ All of request types have equivalent methods that you can use. In a typical C.R. NOTE: Functional tests do not verify whether the specified request type is accepted by the action, we're more concerned with the result. Request tests exist for this use case to make your tests more purposeful. +### Functional Tests with JSON + +When testing a request, Action Dispatch serializes the `:params` argument based on an optional `:as` argument that specifies the content type. For example, Action Dispatch encodes the `:params` argument into the request body as JSON. When asserting the response, the [`ActionDispatch::TestResponse#parsed_body`](https://api.rubyonrails.org/classes/ActionDispatch/TestResponse.html#method-i-parsed_body) method parses the response body into a [`HashWithIndifferentAccess`](https://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html): + +```ruby +class ArticlesControllerTest < ActionDispatch::IntegrationTest + test "should create an Article with JSON" do + post articles_path, params: { article: { body: "Rails is awesome!", title: "Hello Rails" } }, as: :json + + assert_equal "Rails is awesome!", response.parsed_body[:body] + assert_equal "Hello Rails", response.parsed_body[:title] + end +end +``` + +Since `response.parsed_body` returns a `HashWithIndifferentAccess` for JSON responses, tests can combine Ruby [pattern matching](https://docs.ruby-lang.org/en/master/syntax/pattern_matching_rdoc.html) with Minitest's [`assert_pattern`](https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_pattern) assertion: + +```ruby +class ArticlesControllerTest < ActionDispatch::IntegrationTest + test "should create an Article with JSON" do + post articles_path, params: { article: { body: "Rails is awesome!", title: "Hello Rails" } }, as: :json + + assert_pattern { response.parsed_body => { body: "Rails is awesome!", title: /hello rails/i } } + end +end +``` + +Test request encoding and response parsing is configurable, and can be configuring with [`ActionDispatch::IntegrationTest.register_encoder`](https://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest/Behavior/ClassMethods.html#method-i-register_encoder). The `register_encoder` method accepts a two callable options. The `:param_encoder` option controls how the `:params` option is encoded into the request, and the `:response_parser` option controls how the body is parsed from the response. + ### Testing XHR (Ajax) Requests To test Ajax requests, you can specify the `xhr: true` option to `get`, `post`,