How you're testing Live Views in isolation?

Hello! Recently I’ve been working with the LiveViews, they are awesome, very easy, very simple, and very fast to build.

But what I not understanding at all is what is the best way to test a Live View, I know that most of the common tests are validating the HTML output just to see if a label is there or not. That’s nice. But I’d like to test the assigns map itself, my intuition tell me that could be great test the LiveView state, check the state when the LV is mounted, when there is a handle_params, when there is a handle_event, when there is a handle_info, etc…

Looking into this I tried to do something like check the process state and try to update it:

%{socket: %{assigns: assigns}} = :sys.get_state(view.pid)
      :sys.replace_state(view.pid, fn lv_view ->
        update_in(lv_view.socket.assigns, &Map.merge(&1, %{
          data: %{...}
        }))
      end)

This works, but I’m not convinced at all of implement this in my tests. Why I want this? Because sometimes a LV needs modules that are not ready, so I’d like to design in isolation just passing data.

Other option is explore a way to pass a function in the assigns and pass a function to mock data like:

assign_new(socket, :data, fn -> MyApp.get_data() end)
{:ok, view, _html} =
  live_isolated(conn, MyAppWeb.MyLive,
    assigns: %{data: %{...}}
  )

Maybe I’m not understanding something about how to test a LV. Maybe I’m just a crazy guys trying this ideas… but hey, maybe some of you have different approaches to do this.

Thanks for reading.

I really don’t see a reason to test the assigns as the rendered output should reflect the assigns.

4 Likes

This takes me back to early LV when the testing story left a lot to be decided. Today not so, use the element and render_* API, no need to fiddle with this. And if you’re broadcasting then you can start multiple live views so you can test how it would be to have multiple tabs open for a user or two different users!

1 Like

Yeah, I see this mistake made often. Assigns should be considered an “implementation detail”, and testing them leads to a lot of brittleness. When it comes to testing one should test the end result, which in this case is the thing that gets rendered (or not).

1 Like

How I imagine this is that a LV have an initial state, then something happens, that is updating that state, etc… so I was thinking about the possibility to test every change. But I didn’t find nothing related to this, so probably it’s because it doesn’t worth it at all :sweat_smile:

The view is a function of the state. The state itself doesn’t matter. What the client sees does.

As others have eluded in the thread, when testing live views or more general writing tests for our Phoenix application, we’re writing integration tests, so we want to be at the edge and not overthink this.

In this specific case what is rendered is what you want to assert.

What we don’t want to test is phoenix, liveview or some internal state.

I may not be understanding your comment, but I routinely inspect the HTML in tests to a much deeper degree than just looking at a label. I assume the examples of assert html =~ "Click me" are simplified to save space in the docs. I routinely assert on an HTML table’s full contents, assert on the enabled/disabled status of an entire page’s buttons, etc.