phoenix_integration v0.3.0 PhoenixIntegration

Lightweight server-side integration test functions for Phoenix. Works within the existing Phoenix.ConnTest framework and emphasizes both speed and readability.

Configuration

You need to tell phoenix_integration what Endpoint to use for the request calls to work. To do this, add the following to your config/test.exs file

config :phoenix_integration,
  endpoint: MyApp.Endpoint

Where MyApp is the name of your app.

Overview

phoenix_integration provides two assertion and six request functions to be used alongside the existing get, post, put, patch, and delete utilities inside of a Phoenix.ConnTest test suite.

The goal is to chain together a string of requests and assertions that thouroughly exercise your application in as lightweight and readable manner as possible.

Each function accepts a conn and some other data, and returns a conn intended to be passed into the next function via a pipe.

Examples

test "Basic page flow", %{conn: conn} do
  # get the root index page
  get( conn, page_path(conn, :index) )
  # click/follow through the various about pages
  |> follow_link( "About Us" )
  |> follow_link( "Contact" )
  |> follow_link( "Privacy" )
  |> follow_link( "Terms of Service" )
  |> follow_link( "Home" )
  |> assert_response( status: 200, path: page_path(conn, :index) )
end

test "Create new user", %{conn: conn} do
  # get the root index page
  get( conn, page_path(conn, :index) )
  # click/follow through the various about pages
  |> follow_link( "Sign Up" )
  |> follow_form( %{ user: %{
        name: "New User",
        email: "user@example.com",
        password: "test.password",
        confirm_password: "test.password"
      }} )
  |> assert_response(
      status: 200,
      path: page_path(conn, :index),
      html: "New User" )
end

Simulate multiple users

Since all user state is held in the conn that is being passed around (just like when a user is hitting your application in a browser), you can simulate multiple users simply by tracking separate conns for them.

In the example below, I’m assuming an application-specific test_sign_in function, which itself uses the follow_* functions to sign a given user in.

Notice how user_conn is tracked and reused. This keeps the state the user builds up as the various links are followed, just like it would be when a proper browser is used.

Example

test "admin grants a user permissions", %{conn: conn, user: user, admin: admin} do
  # sign in the user and admin
  user_conn = test_sign_in( conn, user )
  admin_conn = test_sign_in( conn, admin )

  # user can't see a restricted page
  user_conn = get( user_conn, page_path(conn, :index) )
  |> follow_link( "Restricted" )
  |> assert_response( status: 200, path: session_path(conn, :new) )
  |> refute_response( body: "Restricted Content" )

  # admin grants the user permission
  get( admin_conn, page_path(conn, :index) )
  |> follow_link( "Admin Dashboard" )
  |> follow_form( %{ user: %{
        permissoin: "ok_to_do_thing"
      }} )
  |> assert_response(
      status: 200,
      path: admin_path(conn, :index),
      html: "Permission Granted" )

  # the user should now be able to see the restricted page
  get( user_conn, page_path(conn, :index) )
  |> follow_link( "Restricted" )
  |> assert_response(
      status: 200,
      path: restricted_path(conn, :index),
      html: "Restricted Content"
    )
end

Tip

You can intermix IO.inspect calls in the pipe chain to help with debugging. This will print the current state of the conn into the console.

test "Basic page flow", %{conn: conn} do
  # get the root index page
  get( conn, page_path(conn, :index) )
  |> follow_link( "About Us" )
  |> IO.inspect
  |> follow_link( "Home" )
  |> assert_response( status: 200, path: page_path(conn, :index) )
end

I like to use assert_response pretty heavily to make sure the content I expect is really there and to make sure I am traveling to the right locations.

  test "Basic page flow", %{conn: conn} do
    get(conn, page_path(conn, :index) )
    |> assert_response(
        status: 200,
        path: page_path(conn, :index),
        html: "Test App"
      )
    |> follow_link( "About" )
    |> assert_response(
        status: 200,
        path: about_path(conn, :index),
        html: "About Test App"
      )
    |> follow_link( "Contact" )
    |> assert_response(
        status: 200,
        path: about_path(conn, :contact),
        html: "Contact"
      )
    |> follow_link( "Home" )
    |> assert_response(
        status: 200,
        path: page_path(conn, :index),
        html: "Test App"
      )
  end

What phoenix_integration is NOT

phoenix_integration is not a client-side acceptence test suite. It does not use a real browser and does not exercise javascript code that lives there. It’s focus is on fast, readable, server-side integration.

Try using a tool like Hound for full-stack integration tests.