Class: ActionDispatch::IntegrationTest

Inherits:
ActiveSupport::TestCase show all
Includes:
Behavior, TestProcess::FixtureFile
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 
    # 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 Rails Testing Guide for more.

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, #root_session

Method Summary

Methods included from Behavior

#app, #document_root_element

Methods included from ActiveSupport::Concern

#append_features, #class_methods, extended, #included, #prepend_features, #prepended

Methods included from ActionController::TemplateAssertions

#assert_template

Methods included from ActionDispatch::Integration::Runner

#assertions, #assertions=, #before_setup, #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, #setup, #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

#inspect, parallelize, parallelize_setup, parallelize_teardown, test_order, test_order=

Methods included from ActiveSupport::Testing::Declarative

#test

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::ConstantStubbing

#stub_const

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, #assert_raises

Methods included from ActiveSupport::Testing::TaggedLogging

#before_setup

Dynamic Method Handling

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