How do I test my LiveView

I have a Phoenix LiveView app that I want to write tests for.

The app is a game and the state of the game is stored in a struct on socket assigns.

With it being a game it’s difficult to replicate the user action to get the page in the state that I want to write assertions on.

Instead I planned on setting the game struct on socket assigns in the test and then asserting that the page is as expected. My test looks something like this:

test "it displays when the game is won", %{conn: conn} do
  conn = assign(conn, :game, %Game{state: :won})
  {:ok, view, html} = live(conn, "/")
  assert render(view) =~ "Won!"
end

When I inspect the socket within the mount method of my LiveView module it doesn’t seem to have the values that I have setup in my test. For reference, the mount method creates a new game and assigns it to the socket.

  def mount(_params, _session, socket) do
    {:ok, new_game(socket)}
  end

Is what I’m trying to do incorrect? Should I be writing assertions for just the view instead, does the mount method need to change so I can pass in different game states (some form of dependency injection), or is there a better way of doing writing these tests

1 Like

Yes and you should notice it seeing your code.

It’s not socket - it’s called conn. The conn is used to store standard HTTP request and response information. The socket is used inside LiveView to store assigns and render template using it.

I was never trying something like that, so I’m not sure what’s the best way, but I have an idea. Firstly let’s take a look at documentation:

LiveViews are GenServer’s under the hood, and can send and receive messages just like any other server. To test the side effects of sending or receiving messages, simply message the view and use the render function to test the result:

send(view.pid, {:set_temp, 50})
assert render(view) =~ "The temperature is: 50℉"

Source: Testing regular messages

Unfortunately using send/2 we cannot return any data directly. However we can call it, so we could use this way to communicate with a process. For this we need to write something like:

def handle_info({:test_debug_assign, pid, name}, socket) do
  send(pid, {:test_debug_assign, socket.assigns[name]})
  {:noreply, socket}
end

With this all we need to do is to send a message and receive the response:

# expected_value =
send(view.pid, {:test_debug_assign, self(), :some_name})
assert_receive {:test_debug_assign, ^expected_value}

Helpful resources

  1. send and receive guide
  2. Phoenix.LiveViewTest.View struct
  3. Kernel.send/2 function
  4. ExUnit.Assertions.assert_receive/3 macro
  5. Testing regular messages documentation
1 Like