PhoenixTest
PhoenixTest is a testing library that allows you to run your feature tests the same way regardless of whether your page is a LiveView or a static view.
It also handles navigation between LiveView and static pages seamlessly. So, you don't have to worry about what type of page you're visiting. Just write the tests from the user's perspective.
Thus, you can test a flow going from static to LiveView pages and back without having to worry about the underlying implementation.
This is a sample flow:
test "admin can create a user", %{conn: conn} do
conn
|> visit("/")
|> click_link("Users")
|> fill_form("#user-form", name: "Aragorn", email: "aragorn@dunedan.com")
|> click_button("Create")
|> assert_has(".user", "Aragorn")
endNote that PhoenixTest does not handle JavaScript. If you're looking for something that supports JavaScript, take a look at Wallaby.
Why PhoenixTest?
Lately, if I'm going to have a page that uses some JavaScript, I use LiveView. If the page is going to be completely static, I use regular controllers + views/HTML modules.
The problem is that they have vastly different testing strategies.
If I use LiveView, we have a great set of helpers. But if a page is static, we
have to resort to controller testing that relies solely on html_response(conn, 200) =~ "Page title" for assertions.
Instead, I'd like to have a unified way of testing Phoenix apps -- when they don't have JavaScript.
That's where PhoenixTest comes in.
It's the one way to test your Phoenix apps regardless of live or static views.
Setup
PhoenixTest requires Phoenix 1.7+ and LiveView 0.20+. It may work with
earlier versions, but I have not tested that.
Installation
Add phoenix_test to your list of dependencies in mix.exs:
def deps do
[
{:phoenix_test, "~> 0.1.0", only: test, runtime: false}
]
endConfiguration
In config/test.exs specify the endpoint to be used for routing requests:
config :phoenix_test, :endpoint, MyApp.Endpoint
Adding a FeatureCase
PhoenixTest helpers can be included via import PhoenixTest.
But since each test needs a conn struct to get started, you'll likely want to
set up a few things before that.
To make that easier, it's helpful to create a FeatureCase module that can be
used from your tests (replace MyApp with your app's name):
defmodule MyAppWeb.FeatureCase do
@moduledoc """
This module defines the test case to be used by tests that require setting up
a connection to test feature tests.
Such tests rely on `PhoenixTest` and also import other functionality to
make it easier to build common data structures and interact with pages.
Finally, if the test case interacts with the database, we enable the SQL
sandbox, so changes done to the database are reverted at the end of every
test. If you are using PostgreSQL, you can even run database tests
asynchronously by setting `use MyAppWeb.FeatureCase, async: true`, although
this option is not recommended for other databases.
"""
use ExUnit.CaseTemplate
using do
quote do
use MyAppWeb, :verified_routes
import MyAppWeb.FeatureCase
import PhoenixTest
end
end
setup tags do
MyApp.DataCase.setup_sandbox(tags)
{:ok, conn: Phoenix.ConnTest.build_conn()}
end
end
Note that we assume your Phoenix project has a
MyApp.DataCase.setup_sandbox(tags) function. If it doesn't, and you want to
use Ecto's Sandbox (highly recommended if you're testing with Ecto), update your
setup to this:
setup tags do
pid = Ecto.Adapters.SQL.Sandbox.start_owner!(MyApp.Repo, shared: not tags[:async])
on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
endUsage
Now, you can create your tests like this:
# test/my_app_web/features/admin_can_create_user_test.exs
defmodule MyAppWeb.AdminCanCreateUserTest do
use MyAppWeb.FeatureCase, async: true
test "admin can create user", %{conn: conn} do
conn
|> visit("/")
|> click_link("Users")
|> fill_form("#user-form", name: "Aragorn", email: "aragorn@dunedain.com")
|> click_button("Create")
|> assert_has(".user", "Aragorn")
endSponsors
Made possible thanks to:
<figure> <img height="100" width="100" src="https://www.elixirstreams.com/assets/images/elixir-streams-logo-transparent.png"> <figcaption>Elixir Streams</figcaption> </figure>