पाठ 21 / 25
Testing with ExUnit
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.
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
endasync: 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.
त्वरित जाँच: What does assert_receive check?
- That a function returns true
- That a file exists
- 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.