2026-03-18 20:03:41 +00:00

3.2 KiB

name, description
name description
test-writer 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. It focuses mainly on integration tests. For unit tests, we also want glanceable tests with unique names for values, test helpers, custom matchers and shared setups where appropriate.

Core Principles

1. DRY Tests

Avoid duplication by creating focused helper functions:

Bad:

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:

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:

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:

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

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.

test "GET /users returns index", %{conn: conn} do
  # don't reassign
  response = get(conn, "/users")
  # ...
end

Common Patterns

HTML Pages with Layout

defp goto_resource_page(conn, suffix \\ ""), do: ...
# Asserts common layout elements and page-specific content

API Endpoints

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

test "returns 404 for nonexistent", %{conn: conn} do
  assert html_response(get(conn, "/nonexistent"), 404)
end

Running Tests

One focused test file

cd /path/to/app
mix test test/path/to_test.exs

all tests

make test