Elixir Setup and Fixtures
A setup block is code that runs before a test and hands that test its starting values. Those starting values are called fixtures.
A setup block hands the test its values
The setup block returns a list of numbers. The test reads that list from its context and adds it.
ExUnit.start(seed: 0, auto_run: false, formatters: [])
defmodule NumbersTest do
use ExUnit.Case
setup do
{:ok, numbers: [10, 20, 30]}
end
test "adds the numbers", %{numbers: numbers} do
assert Enum.sum(numbers) == 60
end
end
result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")Shared values and per-test values combine
The setup_all block runs once and shares a base value. The setup block builds a total from it for every test.
ExUnit.start(seed: 0, auto_run: false, formatters: [])
defmodule BaseTest do
use ExUnit.Case
setup_all do
{:ok, base: 10}
end
setup %{base: base} do
{:ok, total: base + 5}
end
test "uses both values", %{total: total} do
assert total == 15
end
test "uses the shared value", %{base: base} do
assert base == 10
end
end
result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")One setup block can hand over several values
The setup block returns a name and an age. Each test reads only the value it needs from the context.
ExUnit.start(seed: 0, auto_run: false, formatters: [])
defmodule PersonTest do
use ExUnit.Case
setup do
{:ok, name: "Ana", age: 31}
end
test "reads the name", %{name: name} do
assert name == "Ana"
end
test "reads the age", %{age: age} do
assert age == 31
end
end
result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")Example
ExUnit.start(seed: 0, auto_run: false, formatters: [])
defmodule ScoresTest do
use ExUnit.Case
setup_all do
{:ok, scores: [4, 5, 6]}
end
setup %{scores: scores} do
{:ok, total: Enum.sum(scores), count: length(scores)}
end
test "adds the scores", %{total: total} do
assert total == 15
end
test "counts the scores", %{count: count} do
assert count == 3
end
test "reads the first score", %{scores: scores} do
assert List.first(scores) == 4
end
end
result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")Key points
- Fixture: a starting value a test needs, such as a list or a count.
- Setup: a setup block builds the fixture fresh for every test.
- Context: the map a setup block returns, which the test reads through its second argument.
- Setup_all: a setup_all block runs once for the whole module and shares its values with every test.
- Isolation: a fresh fixture per test keeps one test from changing another test's starting values.
In this example, a setup_all block shares a list of scores with every test, and a setup block derives a total and a count from that list for each test. Three tests check the total, the count, and the first score, and all three pass.
Note
setup_all values are shared, so a test must not change them.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does a setup block return so the test can read its values?