Testing with ExUnit
ExUnit is Elixir’s built-in testing framework. It ships with the
language, and every project created by
mix new already has a working test setup. Writing a
test is often the first thing you do when you add a new feature.
Here is the smallest possible test:
defmodule MathTest do
use ExUnit.Case
test "one plus one is two" do
assert 1 + 1 == 2
end
end
Run it with mix test and you see a single green dot.
Starting a New Project
The easiest way to follow along is to create a fresh project:
$ mix new demo
$ cd demo
mix new creates a test/ directory with two files:
-
test/test_helper.exsstarts ExUnit when you runmix test. -
test/demo_test.exsis a sample test module.
Open test/demo_test.exs and you will see this:
defmodule DemoTest do
use ExUnit.Case
doctest Demo
test "greets the world" do
assert Demo.hello() == :world
end
end
Run it:
$ mix test
Compiling 1 file (.ex)
Generated demo app
.
Finished in 0.01 seconds
1 test, 0 failures
Writing Your Own Test
Let’s give Demo something worth testing. Replace the body of
lib/demo.ex:
defmodule Demo do
def add(a, b), do: a + b
end
And write a test for it in test/demo_test.exs:
defmodule DemoTest do
use ExUnit.Case
test "add/2 sums two integers" do
assert Demo.add(2, 3) == 5
end
test "add/2 works with negative numbers" do
assert Demo.add(-1, 1) == 0
end
end
mix test now reports two passing tests.
assert and refute
The two macros you will use most are assert and refute:
test "assert and refute" do
assert 1 + 1 == 2
refute 1 + 1 == 3
end
When an assertion fails, ExUnit prints a detailed message that shows
both sides of the comparison, which is why you write the comparison
inline instead of calling a helper like equal?/2.
Grouping Tests with describe
Use describe to group related tests under a shared label. The label
shows up in the test output, which makes failures easier to locate:
defmodule DemoTest do
use ExUnit.Case
describe "add/2" do
test "sums two positive integers" do
assert Demo.add(2, 3) == 5
end
test "sums two negative integers" do
assert Demo.add(-2, -3) == -5
end
end
end
Shared Setup with setup
If several tests need the same starting point, put the shared work in
a setup block. The map you return is merged into the test context:
defmodule CartTest do
use ExUnit.Case
setup do
cart = %{apples: 2, oranges: 3}
%{cart: cart}
end
test "cart has two apples", %{cart: cart} do
assert cart.apples == 2
end
test "cart has three oranges", %{cart: cart} do
assert cart.oranges == 3
end
end
setup runs before each test. There is also setup_all that runs
once before the whole module, for expensive work you only want to do
once.
Running Part of the Suite
mix test has a few shortcuts you will use every day:
$ mix test (1)
$ mix test test/demo_test.exs (2)
$ mix test test/demo_test.exs:12 (3)
$ mix test --failed (4)
$ mix test --stale (5)
| 1 | Run everything. |
| 2 | Run the tests in a single file. |
| 3 | Run only the test whose test line is on line 12. |
| 4 | Re-run only the tests that failed last time. |
| 5 | Re-run only the tests whose code or dependencies changed. |