# Structure context tests with `describe` blocks

Phoenix context test files grow fast. A single `accounts_test.exs` can easily reach 300+ lines, and without structure it becomes hard to find tests, spot missing coverage, or understand what a function is supposed to do.

`ExUnit.Case` has a `describe/2` macro built in. Use it to mirror your context's public API:

```elixir
defmodule MyApp.AccountsTest do
  use MyApp.DataCase, async: true

  alias MyApp.Accounts

  describe "get_user/1" do
    test "returns the user when found" do
      user = user_fixture()
      assert Accounts.get_user(user.id) == user
    end

    test "returns nil when not found" do
      refute Accounts.get_user(-1)
    end
  end

  describe "create_user/1" do
    test "creates a user with valid attributes" do
      assert {:ok, user} = Accounts.create_user(%{email: "alice@example.com"})
      assert user.email == "alice@example.com"
    end

    test "returns an error changeset with invalid attributes" do
      assert {:error, %Ecto.Changeset{}} = Accounts.create_user(%{email: nil})
    end
  end

  describe "delete_user/1" do
    test "deletes the user" do
      user = user_fixture()
      assert {:ok, _} = Accounts.delete_user(user)
      refute Accounts.get_user(user.id)
    end
  end
end
```

Each `describe` block becomes its own named scope. Test names in output include the block name, so failures read as `"create_user/1 returns an error changeset with invalid attributes"` — immediately actionable.

A few practical benefits:

- **Coverage gaps become obvious** — if `describe "update_user/1"` doesn't exist, you know it's untested
- **Setup is scoped** — use `setup` inside a `describe` block and it only runs for those tests
- **Tags apply to the group** — `@describetag :integration` skips or includes the whole block at once

```elixir
describe "send_welcome_email/1" do
  @describetag :integration

  test "sends an email to the user" do
    user = user_fixture()
    assert {:ok, _} = Accounts.send_welcome_email(user)
  end
end
```

One `describe` block per public function is a good default. It won't stay perfectly organised forever, but it gives every test a home and makes the file scannable at a glance.


---

Created by: almirsarajcic
Date: March 11, 2026
URL: https://elixirdrops.net/d/wY7TeKHm
