Class: ActionDispatch::IntegrationTest
Relationships & Source Files | |
Namespace Children | |
Modules:
| |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
::ActiveSupport::TestCase ,
::ActiveSupport::Testing::Declarative ,
Minitest::Test
|
|
Instance Chain:
self,
UrlOptions ,
Routing::UrlFor ,
Routing::PolymorphicRoutes ,
Behavior ,
Integration::Runner ,
Assertions ,
Rails::Dom::Testing::Assertions,
Assertions::RoutingAssertions ,
Assertions::ResponseAssertions ,
TestProcess::FixtureFile ,
::ActiveSupport::TestCase ,
::ActiveSupport::Testing::FileFixtures ,
::ActiveSupport::Testing::TimeHelpers ,
::ActiveSupport::Testing::Assertions ,
::ActiveSupport::Testing::SetupAndTeardown ,
Minitest::Test
|
|
Inherits: |
ActiveSupport::TestCase
|
Defined in: | actionpack/lib/action_dispatch/testing/integration.rb |
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 get/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 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 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 parsed_body
.
Consult the ::Rails
Testing Guide for more.
Constant Summary
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
.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
Integration::Runner
- Included
::ActiveSupport::TestCase
- Inherited
Instance Method Summary
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, default_url_options and the routes defined in routes.rb. |
#url_options | Hook overridden in controller to add request information with |
#_routes_context, #_with_routes |
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. |
Behavior
- Included
Integration::Runner
- Included
#create_session, #initialize, #integration_session, | |
#open_session | Open a new session instance. |
#reset! | Reset the current session. |
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. |
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: |
TestProcess::FixtureFile
- Included
#fixture_file_upload | Shortcut for |
::ActiveSupport::TestCase
- Inherited
#assert_no_match, #assert_not_empty, #assert_not_equal, #assert_not_in_delta, #assert_not_in_epsilon, #assert_not_includes, #assert_not_instance_of, #assert_not_kind_of, #assert_not_nil, #assert_not_operator, #assert_not_predicate, #assert_not_respond_to, #assert_not_same, | |
#assert_raise | test/unit backwards compatibility methods. |
#method_name |
::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 |
::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. |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class ActionDispatch::Integration::Runner