PhoenixIntegration (phoenix_integration v0.9.2) View Source
Lightweight server-side integration test functions for Phoenix. Works within the existing Phoenix.ConnTest framework and emphasizes both speed and readability.
Configuration
Step 1
You need to tell phoenix_integration which endpoint to use. Add the following to your phoenix application's config/test.exs
file.
config :phoenix_integration,
endpoint: MyApp.Endpoint
Where MyApp is the name of your application.
Do this up before compiling phoenix_integration as part of step 2. If you change the endpoint in the config file, you will need to recompile the phoenix_integration dependency.
Phoenix_integration will produce warnings if your HTML likely doesn't do what you meant. (For example, it will warn you if two text fields have the same name.) You can turn those off by adding warnings: false
to the config.
Step 2
Add PhoenixIntegration to the deps section of your application's mix.exs
file
defp deps do
[
# ...
{:phoenix_integration, "~> 0.8", only: :test}
# ...
]
end
Don't forget to run mix deps.get
Step 3
Create a test/support/integration_case.ex file. Mine simply looks like this:
defmodule MyApp.IntegrationCase do
use ExUnit.CaseTemplate
using do
quote do
use MyApp.ConnCase
use PhoenixIntegration
end
end
end
Alternately you could place the call to use PhoenixIntegration
in your conn_case.ex file. Just make sure it is after the definition of @endpoint
.
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.