guide, phoenix, wallaby, elixir, 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

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.22.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/config.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

Husband, Father & Software Developer who tinkers with writing and building things.
  • Hamilton, ON