Getting Started with ExUnit
ExUnit is Elixir's built-in test framework, included with every Elixir installation — no external dependency required to get started. A test file typically ends in _test.exs, starts with ExUnit.start() (handled automatically by mix test in a Phoenix/Mix project's test_helper.exs), and defines a module using ExUnit.Case, containing one or more test blocks.
Cricket analogy: ExUnit is like the nets session before a big match — a controlled environment where you throw deliveries (test cases) at your technique before it's tested for real.
Writing Assertions
Inside a test block, assert/1 checks that an expression is truthy, while refute/1 checks that it's falsy; both produce detailed, human-readable failure messages showing the actual vs. expected values when they fail — for instance assert result == 4 will print both sides if result was actually 5. assert_raise/2 checks that a specific exception is raised, and assert_receive/2 checks that a message arrives in a process's mailbox within a timeout.
Cricket analogy: Writing assert result == 4 is like appealing for LBW — you're asserting a specific claim and the umpire (test runner) either upholds it or rules not out.
defmodule MyApp.MathTest do
use ExUnit.Case, async: true
describe "add/2" do
test "adds two positive integers" do
assert MyApp.Math.add(2, 2) == 4
end
test "raises on non-numeric input" do
assert_raise ArithmeticError, fn ->
MyApp.Math.add(2, "two")
end
end
end
endSetup, Fixtures, and Tags
A setup block runs before every test in the module, typically returning a map of context values (like a freshly inserted database record) that ExUnit merges into each test's parameters. setup_all runs once before all tests in the module instead of before each one, useful for expensive, read-only fixtures. Tags, applied with @tag, let you mark tests (e.g., @tag :slow or @tag :external) and then include or exclude them at run time with mix test --exclude slow.
Cricket analogy: A setup block is like preparing the pitch report before play — rolling the surface, checking moisture — so every test that follows starts from the same known conditions.
Doctests and Mocking
A doctest is a code example embedded in a module's @doc string, written in iex> prompt format, that ExUnit can execute and verify automatically via doctest MyApp.Math in a test module — this guarantees your documentation examples never silently go stale. For isolating external dependencies, the Elixir community commonly favors explicit behaviours with Mox, defining a contract via @behaviour and swapping in a mock implementation configured per test environment, rather than dynamically patching modules at runtime.
Cricket analogy: A doctest is like a player demonstrating a shot in a coaching manual — the example itself is verified live, so the manual can't drift from what actually works.
Use async: true on ExUnit.Case for tests that don't share mutable global state (like a database) — Phoenix's Ecto.Adapters.SQL.Sandbox makes this safe even for database tests by giving each async test its own transaction that's rolled back at the end, letting your suite run many times faster on multi-core machines.
- ExUnit ships with Elixir; test files end in _test.exs and start with
use ExUnit.Case. - assert/1 and refute/1 are the core assertion macros, producing detailed diff-style failure output.
- assert_raise/2 verifies a specific exception is raised; assert_receive/2 checks process mailbox messages.
- setup and setup_all provide shared context and fixtures to tests, running before each or before all tests respectively.
- @tag lets you categorize tests (e.g., :slow) and selectively run or exclude them via mix test --exclude/--only.
- doctest/1 executes iex> examples in @doc strings, keeping documentation verified against real behavior.
- Mox provides explicit, behaviour-based mocking, favored over dynamic runtime patching.
Practice what you learned
1. What naming convention do ExUnit test files follow?
2. What does setting async: true on an ExUnit.Case allow?
3. What is a doctest?
4. What does Ecto.Adapters.SQL.Sandbox enable in tests?
5. How do you exclude tests tagged @tag :slow when running the suite?
Was this page helpful?
You May Also Like
Ecto and Databases
How Ecto's Repo, Schema, Changeset, and Query modules work together to safely model, validate, and query data in Elixir.
Phoenix Framework Basics
An introduction to Phoenix's router, controllers, contexts, and plug pipeline — the core building blocks of a Phoenix web application.
Mix and Hex Packages
How Elixir's built-in build tool Mix and the Hex package manager handle project structure, dependencies, and production releases.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics