---
title: "Testing LiveView forms"
description: "A walk around some functions of the LiveViewTest module that come in handy for testing forms."
---

# Testing LiveView forms

[Annie Ruygt](https://annieruygtillustration.com/)

This is a post about a few functions to test that our app does what we think it does when a form is submitted. If you want to deploy your Phoenix LiveView app right now, then check out how to [get started](https://fly.io/docs/elixir/). You could be up and running in minutes.

[In](https://fly.io/phoenix-files/sdeb-toggling-element/) [previous](https://fly.io/phoenix-files/liveview-multi-select/) [posts](https://fly.io/phoenix-files/phx-trigger-action/) we’ve used forms for different purposes, but we’ve never talked about how to test that our app does the right thing when a form is submitted. In this post we’ll take a walk around some functions of the [LiveViewTest](https://hexdocs.pm/phoenix_live_view/0.18.0/Phoenix.LiveViewTest.html) module that come in handy for testing forms.

## Testing the phx-submit result

We can test the behavior of our LiveView when the event specified with the `phx-submit` option is handled. The [render_submit/2](https://hexdocs.pm/phoenix_live_view/0.18.0/Phoenix.LiveViewTest.html#render_submit/1) function sends a form submit event and returns the rendered result. It is useful if we want to test the contents of our LiveView right after handling the submit event.

For example, this form checks what happens if we submit a password-update form with a too-short password and a non-matching password confirmation:

[Verified routes](https://twitter.com/chris_mccord/status/1554478915477028864) can be used in tests too, Phoenix 1.7 will bring really cool stuff!

```
  test "Update password: renders errors with invalid data", %{conn: conn} do
    {:ok, lv, _html} = live(conn, ~p"/users/settings")

    result =
      lv
      |> form("#password_update_form", %{
        "current_password" => "invalid",
        "user" => %{
          "password" => "too short",
          "password_confirmation" => "does not match"
        }
      })
      |> render_submit()

    assert result =~ "<h1>Settings</h1>"
    assert result =~ "should be at least 12 character(s)"
    assert result =~ "does not match password"
    assert result =~ "is not valid"
  end 
```

We use the `render_submit` function to try to update the user’s password. After handling the submit event, we can verify that the LiveView’s content contains the expected error messages by asserting the resulting content.

## Testing a form submission with a redirect

The `render_submit` function has two return types. We already saw the first one —the rendered content of the LiveView— but we haven’t mentioned the second one; it returns a tuple of type `{:error, reason}` when the LiveView redirects after handling the `phx-submit` event.

We can use the [follow_redirect/3](https://hexdocs.pm/phoenix_live_view/0.18.0/Phoenix.LiveViewTest.html#follow_redirect/3) function to verify that the LiveView redirects as expected. When there is a redirect it returns a `{:ok, conn}` or `{:ok, live_view, html}` tuple depending on the redirect type we use: a regular redirect or a live redirect respectively. If there is no redirect or if it redirects to the wrong place, it raises an error.

We can also use the resulting `conn` to verify the new rendered page content. For example, we have a LiveView to reset the user’s password after receiving a reset link. If the password is successfully reset, an `:info` message is added and the user is redirected to the login page as follows:

```
{:noreply,
   socket
   |> put_flash(:info, "Password reset successfully.")
   |> redirect(to: ~p"/users/log_in")}
```

We can verify that the LiveView redirects to the correct place and it’s setting the expected `:info` message:

```
test "resets password once", %{conn: conn, token: token, user: user} do
  {:ok, lv, _html} = live(conn, ~p"/users/reset_password/#{token}")

  {:ok, conn} =
    lv
    |> form("#reset_password_form",
      user: %{
        "password" => "new valid password",
        "password_confirmation" => "new valid password"
      }
    )
    |> render_submit() # {:error, {:redirect, %{to: "/users/log_in", flash: "SFMyNTY..."}}}
    |> follow_redirect(conn, ~p"/users/log_in")

  refute get_session(conn, :user_token)
  assert get_flash(conn, :info) =~ "Password reset successfully"
  assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
end
```

The `render_submit` function is called and it returns an `{:error, redirect}` tuple. We pass the redirect tuple and the `conn` to `follow_redirect/3` to perform the underlying request, and the route `~p"/users/log_in"` to verify that it redirects to the correct place.

Finally we use the resulting `conn` to verify that:

1) The user wasn’t logged in.

2) The expected flash message was added.

3) The user’s password was reset.

## Testing an HTTP form submission

In a [previous post](https://fly.io/phoenix-files/phx-trigger-action/), we used a form’s `:action` attribute to execute a controller action from a LiveView.

For example, we have a form to send a username and password to the `~p/users/log_in` route and save the user’s data in session if the authentication is successful:

```
 <.form
  id="login_form"
  :let={f}
  for={:user}
  action={~p"/users/log_in"}
  as={:user}
  >
  <%= label f, :email %>
  <%= email_input f, :email, required: true, value: @email %>

  <%= label f, :password %>
  <%= password_input f, :password, required: true %>

  <div>
    <%= submit "Log in" %>
  </div>
</.form>
```

In this case, we don’t have the `phx-submit` option since we don’t do any validation/process inside the LiveView, but we delegate the whole login process to the `~p"/users/log_in"` controller action.

In LiveView 0.18, the function [submit_form/2](https://hexdocs.pm/phoenix_live_view/0.18.0/Phoenix.LiveViewTest.html#submit_form/2) was added to test the results of sending an HTTP request through the plug pipeline. Let’s use it to test a form submission with valid login params:

```
test "Login user with valid credentials", %{conn: conn} do
  password = "valid_password"
  user = user_fixture(%{password: password})

  {:ok, lv, _html} = live(conn, ~p"/users/log_in")

  form =
    form(lv, "#login_form", user: %{
      email: user.email, 
      password: password, 
      remember_me: true})

  conn = submit_form(form, conn)

  assert redirected_to(conn) == ~p"/"
  assert get_session(conn, :user_token)
  assert get_flash(conn, :info) =~ "Welcome back!"
end
```

The `submit_form/2` function receives a form and executes the HTTP request specified in the form’s `:action` attribute. Then we use the resulting `conn` to check that:

1) This results in the right redirect.

2) The logged user’s token was added to the session.

3) The expected flash message was returned.

## Triggering the submit form action and testing the results

In the same [post](https://fly.io/phoenix-files/phx-trigger-action/), we used the `phx-trigger-action` option to trigger a form `:action` only once a condition had been met. Now we’ll cover how to test that the `phx-trigger-action` attribute has been set to `true`, and we’ll follow the underlying HTTP request to test the action call results.

In this test we submit the same password-update form we used at the beginning of the post, but this time we send valid params. We expect the form `:action` — in charge of updating the user’s token in session — is triggered after validating that the user’s password was successfully updated and setting the `phx-trigger-action` to the form:

```
test "updates the user password", %{conn: conn, user: user} do
  new_password = valid_user_password()

  {:ok, lv, _html} = live(conn, ~p"/users/settings")

  form =
    form(lv, "#password_update_form", %{
      "current_password" => user.password,
      "user" => %{
        "email" => user.email,
        "password" => new_password,
        "password_confirmation" => new_password
      }
    })

  assert render_submit(form) =~ ~r/phx-trigger-action/

  new_password_conn = follow_trigger_action(form, conn)

  assert get_session(new_password_conn, :user_token) != 
           get_session(conn, :user_token)
  assert get_flash(new_password_conn, :info) =~ 
           "Password updated successfully"
  assert Accounts.get_user_by_email_and_password(user.email, new_password)
end
```

We build a form and pass it to the `render_submit/1` function, which returns the new content of our LiveView after handling the `phx-submit` event so that we can assert that the `phx-trigger-action` option is added to the form.

Then, we execute the controller action specified in the `:action` attribute using the [follow_trigger_action/2](https://hexdocs.pm/phoenix_live_view/0.18.0/Phoenix.LiveViewTest.html#follow_trigger_action/2) function, and we use `new_password_conn` to check that:

1) The user’s token was renewed with the new password.

2) The expected flash message was added to the connection.

3) The user’s password was updated.

## Fly.io ❤️ Elixir

Fly.io is a great way to run your Phoenix LiveView app close to your users. It’s really easy to get started. You can be running in minutes.

[Deploy a Phoenix app today! →](https://fly.io/docs/elixir/)

## Wrap-up

In this post we used a few functions of the `LiveViewTest` module to test form submission results when we are using LiveView.

We can use `render_submit` to test that our LiveView executes the correct logic and renders the expected results after handling the submit event that we specify with the `phx_submit` attribute. Additionally, we can use `follow_redirect` together with it for those times when we redirect to another LiveView after handling the submit event.

We can also test the results of executing an HTTP form submission using the `:action` attribute. We use `submit_form` to test the results of sending an HTTP request through the plug pipeline, and `follow_trigger_action` to test the resulting content of the action that is executed once the `phx-trigger-action` attribute is set to true.
