Class: ActionDispatch::IntegrationTest

Inherits:
ActiveSupport::TestCase show all
Includes:
Behavior, TestProcess::FixtureFile
Defined in:
actionpack/lib/action_dispatch/testing/integration.rb,
railties/lib/rails/test_help.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 
  # 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, message)
      post "/say/#{room.id}", xhr: true, params: { message: 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 = (:david)
  # User guest logs in
  guest = (: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 (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 Quails Testing Guide for more.

Direct Known Subclasses

SystemTestCase

Defined Under Namespace

Modules: Behavior, UrlOptions

Constant Summary

Constants included from ActionDispatch::Integration::Runner

ActionDispatch::Integration::Runner::APP_SESSIONS

Constants included from Assertions::ResponseAssertions

Assertions::ResponseAssertions::RESPONSE_PREDICATES

Constants inherited from ActiveSupport::TestCase

ActiveSupport::TestCase::Assertion

Constants included from ActiveSupport::Testing::Assertions

ActiveSupport::Testing::Assertions::UNTRACKED

Instance Attribute Summary

Attributes included from ActionDispatch::Integration::Runner

#app

Instance Method Summary collapse

Methods included from Behavior

#app, #document_root_element

Methods included from ActiveSupport::Concern

#append_features, #class_methods, extended, #included

Methods included from ActionController::TemplateAssertions

#assert_template

Methods included from ActionDispatch::Integration::Runner

#copy_session_variables!, #create_session, #default_url_options, #default_url_options=, #initialize, #integration_session, #open_session, #remove!, #reset!

Methods included from Assertions

#html_document

Methods included from Assertions::RoutingAssertions

#assert_generates, #assert_recognizes, #assert_routing, #method_missing, #with_routing

Methods included from Assertions::ResponseAssertions

#assert_redirected_to, #assert_response

Methods included from TestProcess::FixtureFile

#fixture_file_upload

Methods inherited from ActiveSupport::TestCase

test_order, test_order=

Methods included from ActiveSupport::Testing::Declarative

#test

Methods included from ActiveRecord::TestFixtures

#after_teardown, #enlist_fixture_connections, #run_in_transaction?, #setup_fixtures, #teardown_fixtures

Methods included from ActiveSupport::Testing::FileFixtures

#file_fixture

Methods included from ActiveSupport::Testing::TimeHelpers

#after_teardown, #freeze_time, #travel, #travel_back, #travel_to

Methods included from ActiveSupport::Testing::Deprecation

#assert_deprecated, #assert_not_deprecated, #collect_deprecations

Methods included from ActiveSupport::Testing::Assertions

#assert_changes, #assert_difference, #assert_no_changes, #assert_no_difference, #assert_not, #assert_nothing_raised

Methods included from ActiveSupport::Testing::SetupAndTeardown

#after_teardown

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class ActionDispatch::Integration::Runner

Instance Method Details

#before_setupObject

:nodoc:



48
49
50
51
# File 'railties/lib/rails/test_help.rb', line 48

def before_setup # :nodoc:
  @routes = Quails.application.routes
  super
end