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.
Phoenix Feature Tests with Wallaby and Headless Chrome

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. @tmartin8080

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

Subscribe for Updates