wallaby, phoenix, testing, headless chrome, feature tests

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.

Overview

  • 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 phx.new 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
end

# ...

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
end
end

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

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

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

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
session
|> visit("/")
|> find(css("section.phx-hero"))
|> assert_has(css("h1", text: "Welcome to Phoenix!"))
end
end

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).

Conclusion

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. 

Follow us on twitter for updates: @phxroad

Resources

Author image

About Troy Martin

Ruby, Elixir and Javascript Software Developer.