# Testing with ExUnit — Elixir & Phoenix

Source: https://www.geekswithgeeks.com/en/elixir-phoenix/r-testing

> Write unit, context, controller and LiveView tests.

## Testing is built in

**ExUnit** ships with Elixir. Test files end in `_test.exs`, modules `use ExUnit.Case`, and tests are written with `test "description" do ... end` and **`assert`**, `refute` and `assert_raise`. **`async: true`** runs test modules concurrently, which is safe for pure code and, with the **Ecto SQL Sandbox**, for database tests too, because each test runs in its own transaction that is rolled back. `describe` groups tests, `setup` prepares context, and tags (`@tag :slow`) select or exclude tests. **Doctests** (`doctest Shop.Pricing`) run the examples in your `@doc` strings so documentation stays correct. `assert_receive` checks that a message arrives, ideal for processes and PubSub. Phoenix generates **`DataCase`**, **`ConnCase`** (controller tests with `get(conn, ~p"/api/products")` and `json_response/2`) and **`Phoenix.LiveViewTest`** (`live/2`, `render_click`, `render_submit` and `has_element?`). **Mox** creates mocks for behaviours, keeping test doubles honest. **StreamData** adds property-based testing. Run tests with `mix test`, filter with `mix test test/shop/cart_test.exs:42`, and rerun failures with `--failed`.

## Unit, process and LiveView tests

assert, assert_receive and LiveViewTest helpers.

```elixir
defmodule Shop.PricingTest do
  use ExUnit.Case, async: true
  doctest Shop.Pricing

  describe "with_gst/1" do
    test "adds 18% GST rounded to the nearest paisa" do
      assert Shop.Pricing.with_gst(10_000) == 11_800
    end

    test "rejects negative amounts" do
      assert_raise FunctionClauseError, fn -> Shop.Pricing.with_gst(-1) end
    end
  end
end

defmodule Shop.InventoryTest do
  use ExUnit.Case, async: true

  test "reserving stock broadcasts the new level" do
    Phoenix.PubSub.subscribe(Shop.PubSub, "stock:pen")
    assert {:ok, 8} = Shop.Inventory.reserve("pen", 2)
    assert_receive {:stock_changed, 8}
  end
end

defmodule ShopWeb.StockLiveTest do
  use ShopWeb.ConnCase, async: true
  import Phoenix.LiveViewTest

  test "reserving updates the page", %{conn: conn} do
    {:ok, view, html} = live(conn, ~p"/stock/pen")
    assert html =~ "In stock"

    view |> form("form", %{qty: "3"}) |> render_submit()
    assert render(view) =~ "Reserved 3"
  end
end
```

## async: true by default

Most ExUnit tests can run with `async: true`. Tests that change global state (application environment, named processes, the file system) should use `async: false`, or better, be refactored to avoid global state.

**Quiz:** What does assert_receive check?

- [ ] That a function returns true
- [ ] That a file exists
- [x] That the test process receives a matching message within a timeout
- [ ] That a test is async

*Answer:* That the test process receives a matching message within a timeout. assert_receive waits for a message matching a pattern.
