Phoenix Feature Tests with Wallaby and Headless Chrome

This guide is just an introduction to writing feature tests for Phoenix and we'll likely dive deeper in future articles.

We were recently tasked with creating a test suite using feature tests (a type of integration tests).  There are a few options for libraries which are likely all great, and we may explore others in the future, but we landed on using Wallaby mostly because of the natural syntax.

This guide is just an introduction to writing feature tests for Phoenix and we'll likely dive deeper in future articles.


  • Config greenfield app for feature tests using wallaby and headless chrome
  • Create a few simple tests
  • Run the tests

Install Chromedriver

There are different methods of installing chromedriver locally, so go with whatever suits your environment.

Here's an example of how to install chromedriver using npm.

$ npm i -g chromedriver --chromedriver_version=79.0.3945.36

NOTE: The version of chromedriver should match the version of chrome that you have installed.  Otherwise you'll get session errors.

Create an app

Let's create a new throwaway app to work with:

$ mix app

Then the usual setup:

$ cd app
$ mix ecto.create

Add deps

We only need wallaby so open mix.exs and add the following to deps:

     {:wallaby, "~> 0.23.0", runtime: false, only: :test}

And run:

$ mix deps.get

Configure Wallaby

There are a few configuration steps, the first is to add the following to config/test.exs

config :wallaby,
 driver: Wallaby.Experimental.Chrome,
 chrome: [headless: true] # enable/disable

Notice that Chrome is an experimental feature of Wallaby and that you can toggle headless with this configuration.  There are some situations where it's helpful to see what's going on in the test browser.

Next we're going to ensure that Wallaby is started and set the wallaby `base_url` env variable in test/test_helper.exs:

{:ok, _apps} = Application.ensure_all_started(:wallaby)

Configure Concurrent Testing

From the docs:

If you're testing a Phoenix application with Ecto 2 and a database that supports sandbox mode then you can enable concurrent testing by adding the Phoenix.Ecto.SQL.Sandbox plug to your Endpoint. It's important that this is at the top of endpoint.ex before any other plugs.

Open LIB_PATH/endpoint.ex and add the plug at the beginning:

defmodule AppWeb.Endpoint do
 use Phoenix.Endpoint, otp_app: :app

 if Application.get_env(:your_app, :sql_sandbox) do
   plug Phoenix.Ecto.SQL.Sandbox

# ...

Next, open config/test.exs and configure Phoenix to serve endpoints in tests and enable SQL sandbox:

# ...

config :app, AppWeb.Endpoint,
 http: [port: 4002],
 server: true # --- Enabled

config :app, :sql_sandbox, true # --- Added

config :wallaby,
 driver: Wallaby.Experimental.Chrome,
 chrome: [headless: true]


Your test configuration should look similar to above at this point.

Then in test/test_helper.exs  you can provide some configuration to Wallaby. At minimum, you need to specify a :base_url, so Wallaby knows how to resolve relative paths.


Application.put_env(:wallaby, :base_url, AppWeb.Endpoint.url())

Quick Sanity Check

So now that Wallaby is configured (at least for our simple test cases) let's do a quick sanity check to see if anythings out of whack:

$ mix test

If your tests succeed, then we're good to continue.

Add FeatureCase Helper

We need to add a new case template to include in feature tests.  

Create test/support/feature_case.ex with this content:

defmodule AppWeb.FeatureCase do
 use ExUnit.CaseTemplate      

 using do
   quote do                  
     use Wallaby.DSL          

     alias App.Repo          
     import Ecto              
     import Ecto.Changeset    
     import Ecto.Query        

     import AppWeb.Router.Helpers

 setup tags do                
   :ok = Ecto.Adapters.SQL.Sandbox.checkout(App.Repo)

   unless tags[:async] do
     Ecto.Adapters.SQL.Sandbox.mode(App.Repo, {:shared, self()})

   metadata = Phoenix.Ecto.SQL.Sandbox.metadata_for(App.Repo, self())
   {:ok, session} = Wallaby.start_session(metadata: metadata)
   {:ok, session: session}    

This is taken from the docs and manages some of the hand-wavy business of importing modules and preparing for concurrent tests.  We'll use this module in any feature tests we write.

Write Feature Test

Ok, so after all the configuration, let's get into what we set out to accomplish.

Writing feature tests with Phoenix.

We haven't made any changes to the UI so we're just going to test that the index page is loaded with some predictable element on the page. section.phx-hero

Create a new file at test/app_web/features/index_page_test.exs with the new test:

defmodule AppWeb.Features.IndexPageTest do
 use AppWeb.FeatureCase, async: true
 import Wallaby.Query        

 test "index page has a welcome message", %{session: session} do
   |> visit("/")              
   |> find(css("section.phx-hero"))  
   |> assert_has(css("h1", text: "Welcome to Phoenix!"))

This simple test uses the session created by FeatureCase, visits the page and tests that some key elements are visible on the page using headless chrome.

To run:

$ mix test

Success! You should have a fully green test suite at this point.  (If you don't and get stuck hit us up, and we'll try to help).


This guide was intended to be an intro to writing feature tests for phoenix.  We covered the basics of configuring and using Wallaby and wrote a simple test.

There are many more elements to writing feature tests and we hope to cover more detailed tests in the future.

Let me know if there are any issues with the guide, or if you have any questions.

Troy Martin
Senior Engineer working with Elixir and JS.
Keep in the Loop

Subscribe for Updates

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.