Class: ActionDispatch::IntegrationTest
Overview
An integration test spans multiple controllers and actions, tying them all together to ensure they work together as expected. It tests more completely than either unit or functional tests do, exercising the entire stack, from the dispatcher to the database.
At its simplest, you simply extend IntegrationTest
and write your tests using the Integration::RequestHelpers#get and/or Integration::RequestHelpers#post methods:
require "test_helper"
class ExampleTest < ActionDispatch::IntegrationTest
fixtures :people
def test_login
# get the login page
get "/login"
assert_equal 200, status
# post the login and follow through to the home page
post "/login", params: { username: people(:jamis).username,
password: people(:jamis).password }
follow_redirect!
assert_equal 200, status
assert_equal "/home", path
end
end
However, you can also have multiple session instances open per test, and even extend those instances with assertions and methods to create a very powerful testing DSL that is specific for your application. You can even reference any named routes you happen to have defined.
require "test_helper"
class AdvancedTest < ActionDispatch::IntegrationTest
fixtures :people, :rooms
def test_login_and_speak
jamis, david = login(:jamis), login(:david)
room = rooms(:office)
jamis.enter(room)
jamis.speak(room, "anybody home?")
david.enter(room)
david.speak(room, "hello!")
end
private
module CustomAssertions
def enter(room)
# reference a named route, for maximum internal consistency!
get(room_url(id: room.id))
assert(...)
#...
end
def speak(room, )
post "/say/#{room.id}", xhr: true, params: { message: }
assert(...)
#...
end
end
def login(who)
open_session do |sess|
sess.extend(CustomAssertions)
who = people(who)
sess.post "/login", params: { username: who.username,
password: who.password }
assert(...)
end
end
end
Another longer example would be:
A simple integration test that exercises multiple controllers:
require "test_helper"
class UserFlowsTest < ActionDispatch::IntegrationTest
test "login and browse site" do
# login via https
https!
get "/login"
assert_response :success
post "/login", params: { username: users(:david).username, password: users(:david).password }
follow_redirect!
assert_equal '/welcome', path
assert_equal 'Welcome david!', flash[:notice]
https!(false)
get "/articles/all"
assert_response :success
assert_select 'h1', 'Articles'
end
end
As you can see the integration test involves multiple controllers and exercises the entire stack from database to dispatcher. In addition you can have multiple session instances open simultaneously in a test and extend those instances with assertion methods to create a very powerful testing DSL (domain-specific language) just for your application.
Here’s an example of multiple sessions and custom DSL in an integration test
require "test_helper"
class UserFlowsTest < ActionDispatch::IntegrationTest
test "login and browse site" do
# User david logs in
david = login(:david)
# User guest logs in
guest = login(:guest)
# Both are now available in different sessions
assert_equal 'Welcome david!', david.flash[:notice]
assert_equal 'Welcome guest!', guest.flash[:notice]
# User david can browse site
david.browses_site
# User guest can browse site as well
guest.browses_site
# Continue with other assertions
end
private
module CustomDsl
def browses_site
get "/products/all"
assert_response :success
assert_select 'h1', 'Products'
end
end
def login(user)
open_session do |sess|
sess.extend(CustomDsl)
u = users(user)
sess.https!
sess.post "/login", params: { username: u.username, password: u.password }
assert_equal '/welcome', sess.path
sess.https!(false)
end
end
end
See the [request helpers documentation] (ActionDispatch::Integration::RequestHelpers) for help on how to use get
, etc.
Changing the request encoding
You can also test your JSON API easily by setting what the request should be encoded as:
require "test_helper"
class ApiTest < ActionDispatch::IntegrationTest
test "creates articles" do
assert_difference -> { Article.count } do
post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
end
assert_response :success
assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
end
end
The as
option passes an “application/json” Accept header (thereby setting the request format to JSON unless overridden), sets the content type to “application/json” and encodes the parameters as JSON.
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:
ActionDispatch::IntegrationTest.register_encoder :wibble,
param_encoder: -> params { params.to_wibble },
response_parser: -> body { body }
Where param_encoder
defines how the params should be encoded and response_parser
defines how the response body should be parsed through TestResponse#parsed_body.
Consult the [Rails Testing Guide](guides.rubyonrails.org/testing.html) for more.
Constant Summary
::ActiveSupport::Testing::Assertions
- Included
::ActiveSupport::TestCase
- Inherited
Assertions::ResponseAssertions
- Included
Integration::Runner
- Included
Class Attribute Summary
::ActiveSupport::TestCase
- Inherited
.file_fixture_path, .file_fixture_path?, | |
.test_order | Returns the order in which test cases are run. |
.test_order= | Sets the order in which test cases are run. |
Class Method Summary
::ActiveSupport::TestCase
- Inherited
.fixture_paths | Returns the |
.fixture_paths= | Sets the given path to the fixture set. |
.parallelize | Parallelizes the test suite. |
.parallelize_setup | Set up hook for parallel testing. |
.parallelize_teardown | Clean up hook for parallel testing. |
::ActiveSupport::Testing::Declarative
- Extended
Instance Attribute Summary
Routing::UrlFor
- self
Integration::Runner
- Included
::ActiveSupport::TestCase
- Inherited
::ActiveSupport::Testing::TimeHelpers
- Included
::ActiveSupport::Testing::TaggedLogging
- Included
Instance Method Summary
Assertions::RoutingAssertions::WithIntegrationRouting
- self
UrlOptions
- self
Routing::UrlFor
- self
#initialize, | |
#route_for | Allows calling direct or regular named route. |
#url_for | Generate a URL based on the options provided, |
#url_options | Hook overridden in controller to add request information with |
#_routes_context, #_with_routes, #full_url_for |
Routing::PolymorphicRoutes
- Included
#polymorphic_path | Returns the path component of a URL for the given record. |
#polymorphic_url | Constructs a call to a named RESTful route for the given record and returns the resulting URL string. |
#polymorphic_mapping, #polymorphic_path_for_action, #polymorphic_url_for_action |
Behavior
- Included
TestHelpers::PageDumpHelper
- Included
#save_and_open_page | Saves the content of response body to a file and tries to open it in your browser. |
#html_dump_default_path, #open_file, #save_page |
::ActionController::TemplateAssertions
- Included
Integration::Runner
- Included
#create_session, #initialize, #integration_session, | |
#open_session | Open a new session instance. |
#reset! | Reset the current session. |
#method_missing | Delegate unhandled messages to the current session instance. |
#respond_to_missing?, #before_setup, | |
#copy_session_variables! | Copy the instance variables from the current session instance into the test instance. |
#remove! |
Assertions
- Included
Assertions::RoutingAssertions
- Included
#assert_generates | Asserts that the provided options can be used to generate the provided path. |
#assert_recognizes | Asserts that the routing of the given |
#assert_routing | Asserts that path and options match both ways; in other words, it verifies that |
#method_missing | ROUTES TODO: These assertions should really work in an integration context. |
#with_routing | A helper to make it easier to test different route configurations. |
#create_routes, #fail_on, | |
#recognized_request_for | Recognizes the route for a given path. |
#reset_routes, #setup |
Assertions::ResponseAssertions
- Included
#assert_redirected_to | Asserts that the response is a redirect to a URL matching the given options. |
#assert_response | Asserts that the response is one of the following types: |
#code_with_name, #exception_if_present, #generate_response_message, #location_if_redirected, #normalize_argument_to_redirection, | |
#parameterize | Proxy to to_param if the object will respond to it. |
#response_body_if_short |
TestProcess::FixtureFile
- Included
#file_fixture_upload | Shortcut for ‘Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.file_f ixture_path, path), type)`: |
#fixture_file_upload | Alias for TestProcess::FixtureFile#file_fixture_upload. |
::ActiveSupport::TestCase
- Inherited
#assert_no_match | Alias for: refute_match. |
#assert_not_empty | Alias for: refute_empty. |
#assert_not_equal | Alias for: refute_equal. |
#assert_not_in_delta | Alias for: refute_in_delta. |
#assert_not_in_epsilon | Alias for: refute_in_epsilon. |
#assert_not_includes | Alias for: refute_includes. |
#assert_not_instance_of | Alias for: refute_instance_of. |
#assert_not_kind_of | Alias for: refute_kind_of. |
#assert_not_nil | Alias for: refute_nil. |
#assert_not_operator | Alias for: refute_operator. |
#assert_not_predicate | Alias for: refute_predicate. |
#assert_not_respond_to | Alias for: refute_respond_to. |
#assert_not_same | Alias for: refute_same. |
#method_name, #inspect |
::ActiveSupport::Testing::FileFixtures
- Included
#file_fixture | Returns a |
::ActiveSupport::Testing::TimeHelpers
- Included
#after_teardown, | |
#freeze_time | Calls |
#travel | Changes current time to the time in the future or in the past by a given time difference by stubbing |
#travel_back | Returns the current time back to its original state, by removing the stubs added by |
#travel_to | Changes current time to the given time by stubbing |
#unfreeze_time | |
#simple_stubs |
::ActiveSupport::Testing::ConstantStubbing
- Included
#stub_const | Changes the value of a constant for the duration of a block. |
::ActiveSupport::Testing::Deprecation
- Included
#assert_deprecated | Asserts that a matching deprecation warning was emitted by the given deprecator during the execution of the yielded block. |
#assert_not_deprecated | Asserts that no deprecation warnings are emitted by the given deprecator during the execution of the yielded block. |
#collect_deprecations | Returns the return value of the block and an array of all the deprecation warnings emitted by the given deprecator during the execution of the yielded block. |
::ActiveSupport::Testing::ErrorReporterAssertions
- Included
#assert_error_reported | Assertion that the block should cause at least one exception to be reported to Rails.error. |
#assert_no_error_reported | Assertion that the block should not cause an exception to be reported to Rails.error. |
::ActiveSupport::Testing::Assertions
- Included
#assert_changes | Assertion that the result of evaluating an expression is changed before and after invoking the passed in block. |
#assert_difference | Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block. |
#assert_no_changes | Assertion that the result of evaluating an expression is not changed before and after invoking the passed in block. |
#assert_no_difference | Assertion that the numeric result of evaluating an expression is not changed before and after invoking the passed in block. |
#assert_not | Asserts that an expression is not truthy. |
#assert_nothing_raised | Assertion that the block should not raise an exception. |
#assert_raise | |
#assert_raises | Asserts that a block raises one of |
#_assert_nothing_raised_or_warn, #_callable_to_source_string |
::ActiveSupport::Testing::TestsWithoutAssertions
- Included
::ActiveSupport::Testing::SetupAndTeardown
- Included
::ActiveSupport::Testing::TaggedLogging
- Included
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class ActionDispatch::Integration::Runner