PhoenixTest

repository·main·Indexed 43 days ago

https://github.com/germsvel/phoenix_test

A testing library for Phoenix applications that provides a unified API for writing feature tests across both LiveView and traditional server-rendered static pages. It abstracts the differences between LiveView and controller helpers, offering a consistent interface for user flows with commands like visit/1, click_link/1, and fill_in/2. Includes improved assertions via assert_has/2 for targeting elements with CSS selectors. Version 0.12.1 does not support JavaScript.

Tokens
11.9K
Snippets
59
Records
64
Agent score
56%

What's inside PhoenixTest

  1. How PhoenixTest unifies LiveView and static page testing

    main

    In standard Phoenix testing, LiveView pages are typically tested using live/2 helpers, while static (server-rendered) pages are tested using controller helpers like get/2 and html_response/2. These two approaches have vastly different APIs and make it difficult to test flows that cross the boundary between them.

    PhoenixTest abstracts these differences away, allowing you to use a single set of interaction commands (like visit/1, click_link/1, fill_in/2, and click_button/1) regardless of whether the target page is a LiveView or a traditional controller-based view. This enables testing complete user journeys from a user's perspective.

  2. Note on route validation in version 0.6.0

    main
    Starting with version 0.6.0, PhoenixTest will raise an error if you attempt to navigate to a route that is not defined in your router. This change prevents false positives in tests where a refute_has might pass simply because the test landed on a 404 or 500 error page instead of the intended destination.
  3. Use label-based form helpers (introduced in 0.2.13)

    main

    Version 0.2.13 introduced new form helpers that target elements by their visible labels rather than relying on the underlying data structure or CSS selectors. This decouples tests from implementation details like changeset shapes.

    New Helpers:

    • fill_in/3: Fills an input field based on its label.
    • select/3: Selects an option in a dropdown based on the label and option text.
    • choose/3: Selects a radio button or similar choice.
    • check/3: Checks a checkbox.
    • uncheck/3: Unchecks a checkbox.

    Example Migration:

    Old way (targeting by form structure):

    session
    |> fill_form("form", user: %{name: "Aragorn", admin: true, country: "Arnor"})

    New way (targeting by labels):

    session
    |> fill_in("Name", with: "Aragorn")
    |> check("Admin")
    |> select("Arnor", from: "Countries")

    Note: If your labels are visually hidden (e.g., using Tailwind's sr-only class), these helpers will still work as long as the label is present in the DOM for accessibility.

  4. Migrate `select/3` and `select/4` usage in version 0.6.0

    main

    Version 0.6.0 deprecates using the :from keyword argument to denote a label in select/3 and select/4. To maintain consistency with other form helpers, the label must now be passed as a positional argument, and the option text should be passed using the :option keyword argument.

    Migration Patterns:

    Standard usage:

    - |> select("Option 1", from: "Select Label")
    + |> select("Select Label", option: "Option 1")

    With CSS selector:

    - |> select("#super-select", "Option 1", from: "Select Label")
    + |> select("#super-select", "Select Label", option: "Option 1")
  5. Handle automatic `phx-change` on `upload` in version 0.7.0

    main

    In version 0.7.0, PhoenixTest automatically triggers the phx-change event when using upload helpers to better emulate real Phoenix behavior.

    Action Required: If your tests manually trigger phx-change after an upload, you may now encounter duplicate events. You should remove the manual trigger to avoid this duplication.

  6. Write unified feature tests with PhoenixTest

    main

    PhoenixTest provides a unified API for writing feature tests that works seamlessly across both LiveView pages and static (server-rendered) pages. This allows you to test user flows that transition between different page types (e.g., moving from a static controller-based page to a LiveView page and back) without worrying about the underlying implementation details.

    Note: PhoenixTest does not support JavaScript. For JavaScript-heavy testing, use Wallaby.

    test "admin can create a user", %{conn: conn} do
      conn
      |> visit("/")
      |> click_link("Users")
      |> fill_in("Name", with: "Aragorn")
      |> click_button("Create")
      |> assert_has(".user", "Aragorn")
    end
  7. Scope interactions with `within/3`

    main

    The within function allows you to scope all subsequent interactions to a specific part of the DOM. If the selector targets a nested LiveView (identified by phx-session), within will automatically switch the session's focus to that child LiveView.

    # Scope interactions to a specific container
    PhoenixTest.Live.within(session, ".user-card-123", fn session ->
      PhoenixTest.Live.click_button(session, "Edit")
    end)
  8. Set up a FeatureCase for testing

    main

    To use PhoenixTest helpers conveniently, create a FeatureCase module. This module should include PhoenixTest and set the endpoint. If your project uses Ecto, ensure you include the SQL.Sandbox setup to handle database transactions correctly.

    defmodule MyAppWeb.FeatureCase do
      use ExUnit.CaseTemplate
    
      using do
        quote do
          use MyAppWeb, :verified_routes
          import MyAppWeb.FeatureCase
          import PhoenixTest
    
          @endpoint MyAppWeb.Endpoint
        end
      end
    
      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)
    
        {:ok, conn: Phoenix.ConnTest.build_conn() |> PhoenixTest.put_endpoint(MyAppWeb.Endpoint)}
      end
    end
  9. Set the endpoint using PhoenixTest.put_endpoint/2

    main

    To avoid the requirement of setting @endpoint as a compile-time module attribute in your tests, you can attach the endpoint to the connection at runtime. Use PhoenixTest.put_endpoint/2 during your test setup to store the endpoint in conn.private[:phoenix_endpoint].

    conn =
      Phoenix.ConnTest.build_conn()
      |> PhoenixTest.put_endpoint(MyAppWeb.Endpoint)
  10. Initialize a LiveView session with `PhoenixTest.Live.build/1`

    main

    To start testing a LiveView, use PhoenixTest.Live.build(conn) to create a session object. This initializes the view, starts a watcher for LiveView updates, and tracks the current path. The returned session struct contains the view, watcher, conn, and current_path needed for subsequent interactions.

    # Assuming conn is a valid Plug.Conn from your test setup
    session = PhoenixTest.Live.build(conn)
  11. Submit forms using `submit/1`

    main

    To submit a form after filling it out without explicitly clicking a button (emulating pressing <Enter>), use the submit/1 helper.

    session
    |> fill_in("Name", with: "Aragorn")
    |> check("Admin")
    |> select("Arnor", from: "Countries")
    |> submit()