Browse lessons

Testing Rendering

Testing GreetLive

We’ll test that we can find some text rendered by a LiveView.

If you look at our router, you’ll see we’re mounting a new LiveView in /greet.

# lib/ranger_web/router.ex
live "/greet", GreetLive

Here’s our code found in lib/ranger_web/live/greet_live.ex:

defmodule RangerWeb.GreetLive do
  use RangerWeb, :live_view

  def render(assigns) do
    ~H"""
    <%= if @live do %>
      <h1 class="text-4xl font-bold tracking-tight sm:text-center sm:text-6xl">
        Welcome to Testing LiveView
      </h1>
    <% else %>
      <h1 class="text-4xl font-bold tracking-tight sm:text-center sm:text-6xl">
        Welcome to stateless HTTP
      </h1>
    <% end %>
    """
  end

  def mount(_, _, socket) do
    if connected?(socket) do
      {:ok, assign(socket, :live, true)}
    else
      {:ok, assign(socket, :live, false)}
    end
  end
end

GreetLive conditionally renders text on the screen based on whether or not the LiveView is connected.

LiveView’s life-cycle first makes a stateless HTTP request. Then, it upgrades the connection to a websocket. We’ll see how we can test both scenarios.

Testing GreetLive

Open the corresponding test file located in test/ranger_web/live/greet_live_test.exs.

Since this is our first test, let’s talk about the setup.

Setup required for LiveView tests

# test/ranger_web/live/greet_live_test.exs
defmodule RangerWeb.GreetLiveTest do
  use RangerWeb.ConnCase

  import Phoenix.LiveViewTest
end
  • We use RangerWeb.ConnCase just like we do in controller tests because we need a connection struct in every test to mount the live view.
  • We then import Phoenix.LiveViewTest to get all the helpers we’ll need to mount the live view, interact with it, and assert things about it.

Testing stateless HTTP rendering

This test will check the disconnected state of the LiveView.

test "rendering disconnected state", %{conn: conn} do
  disconnected = conn |> get(~p"/greet")

  assert html_response(disconnected, 200) =~ "Welcome to stateless HTTP"
end

We assert that the HTML string returned has the text "Welcome to stateless HTTP" somewhere in the HTML.

We can run the test with mix test path/to/test/file:

mix test test/ranger_web/live/greet_live_test.exs
.

Finished in 0.1 seconds
1 tests, 0 failures, 0 excluded

Excellent. As you can see, our test found the text our LiveView renders when it is not connected.

Upgrade connection with live/2

LiveViewTest has a live/2 helper that can upgrade the connection. Let’s use that:

test "upgrading to connected state", %{conn: conn} do
  disconnected = conn |> get(~p"/greet")
  {:ok, _view, html} = live(disconnected)

  assert html =~ "Welcome to Testing LiveView"
end

Let’s break the test down:

  • The live/2 helper comes from Phoenix.LiveViewTest. We only pass one argument to live/2 and leave the second as the default (which is nil). That returns a three tuple, {:ok, view, html}.
  • We’ll ignore the view for now. We’ll talk about that in a second.
  • The html returned is the HTML returned from the connected state.
  • We make a similar assertion to the one we made with the disconnected state. In this case we assert that the "Welcome to Testing LiveView" string is somewhere in the HTML blob.

Run the test, and it works!

mix test test/ranger_web/live/greet_live_test.exs
.

Finished in 0.1 seconds
1 tests, 0 failures, 0 excluded

We’re now testing the text that comes from the connected LiveView.

Passing the path to live/2

Typically, we don’t need to test the stateless HTTP request and response.

We can simplify our test by using live/2 and passing the path as the second argument.

live/2 performs the stateless request for us behind the scenes, upgrades the connection, and returns the connected HTML as the third element of the tuple.

test "rendering connected state", %{conn: conn} do
  {:ok, _view, html} = live(conn, ~p"/greet")

  assert html =~ "Welcome to Testing LiveView"
end

Run the test to confirm it works as expected:

mix test test/ranger_web/live/greet_live_test.exs

.

Finished in 0.1 seconds

1 tests, 0 failures, 0 excluded

Using view

Thus far, we’ve only looked at the html portion of the live/2 helper.

But the helper returns a view as a second argument. That view is a struct full of information that will allow us to interact with the LiveView.

We’ll do more of that in future videos, but for now, you will see that view has some information like:

  • the module we’re testing, and
  • the process ID of the LiveView.

But, most importantly, we can pass that view to render functions to render the HTML on the page:

test "rendering with the view", %{conn: conn} do
  {:ok, view, _html} = live(conn, "/greet")

  assert view.module == RangerWeb.GreetLive
  assert is_pid(view.pid)
  assert render(view) =~ "Welcome to Testing LiveView"
end

You might not see much difference between the html rendered and what we get from render(view), but in future lessons, we’ll see how we can interact with the page and re-render the resulting HTML.

For now, you can see the live/2 helper is the key to accessing the world of LiveView testing.