--- name: test-writer description: Writes tests following Elixir/Phoenix best practices. Ensures DRY tests with proper helper functions, no duplicated setup code, and correct parameter defaults. Use when writing or modifying tests. --- # Test Writer Skill ## Overview This skill provides guidelines for writing clean, maintainable Elixir/Phoenix tests following best practices. ## Core Principles ### 1. DRY Tests Avoid duplication by creating focused helper functions: **Bad:** ```elixir test "GET /users returns index", %{conn: conn} do conn = get(conn, "/users") body = html_response(conn, 200) assert body =~ "Users" end test "GET /users/:id returns show", %{conn: conn} do conn = get(conn, "/users/1") body = html_response(conn, 200) assert body =~ "User" end ``` **Good:** ```elixir defp goto_users_page(conn, suffix \\ ""), do: get(conn, "/users" <> suffix) test "GET /users returns index", %{conn: conn} do conn = goto_users_page(conn) assert html_response(conn, 200) =~ "Users" end test "GET /users/:id returns show", %{conn: conn} do conn = goto_users_page(conn, "/1") assert html_response(conn, 200) =~ "User" end ``` ### 2. Separate Helpers for Different Assertion Patterns Don't use conditionals in helpers to handle different cases: **Bad:** ```elixir defp goto_users_page(conn, suffix \\ "", check_title \\ true) do path = "/users" <> suffix conn = get(conn, path) body = html_response(conn, 200) if check_title, do: assert body =~ "Users" assert body =~ "AppLayout" body end ``` **Good:** ```elixir defp goto_users_page(conn, suffix \\ "") do path = "/users" <> suffix conn = get(conn, path) body = html_response(conn, 200) assert body =~ "Users" assert body =~ "AppLayout" body end defp goto_user_page(conn, suffix) do path = "/users" <> suffix conn = get(conn, path) body = html_response(conn, 200) assert body =~ "AppLayout" body end ``` ## Test Structure ### Context Block ```elixir describe "resource name" do # Shared setup in context if needed # test "scenario" do ... end ``` ### Value Aliasing Never reuse value names. Elixir is immutable, but value aliasing is confusing. Use unique, meaningful names for the left hand side of assignments. Or use pipes `|>` to eliminate the need for naming. ```elixir test "GET /users returns index", %{conn: conn} do # don't reassign response = get(conn, "/users") # ... end ``` ## Common Patterns ### HTML Pages with Layout ```elixir defp goto_resource_page(conn, suffix \\ ""), do: ... # Asserts common layout elements and page-specific content ``` ### API Endpoints ```elixir test "GET /api/resource returns JSON", %{conn: conn} do conn = conn |> put_req_header("accept", "application/json") conn = get(conn, "/api/resource") response = json_response(conn, 200) # Assert structure end ``` ### Error Handling ```elixir test "returns 404 for nonexistent", %{conn: conn} do assert html_response(get(conn, "/nonexistent"), 404) end ``` ## Running Tests ### One focused test file ```bash cd /path/to/app mix test test/path/to_test.exs ``` ### all tests ```bash make test ```