🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Test Helpers

A test helper is a shared module that holds setup and checks used by more than one test module. One helper replaces copied setup in every test that uses it.

A helper builds the same starting state

One helper function returns the same sample list. Every test that imports it starts from that list.

ExUnit.start(seed: 0, auto_run: false, formatters: [])

defmodule ListHelper do
  def sample, do: [3, 1, 2]
end

defmodule SampleTest do
  use ExUnit.Case
  import ListHelper

  test "helper supplies the sample" do
    assert sample() == [3, 1, 2]
    assert length(sample()) == 3
  end
end

result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")

A helper holds a shared check

The shared check asserts sorted order and returns the length. Tests call it instead of repeating the assertion.

ExUnit.start(seed: 0, auto_run: false, formatters: [])

defmodule ListHelper do
  import ExUnit.Assertions

  def check_sorted(list) do
    assert list == Enum.sort(list)
    length(list)
  end
end

defmodule SortedTest do
  use ExUnit.Case
  import ListHelper

  test "helper checks sorted order" do
    assert check_sorted([1, 2, 3]) == 3
  end
end

result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")

Two test modules reuse one helper

Both modules import the same helper. Neither module copies the sample list.

ExUnit.start(seed: 0, auto_run: false, formatters: [])

defmodule ListHelper do
  def sample, do: [3, 1, 2]
end

defmodule FirstTest do
  use ExUnit.Case
  import ListHelper

  test "first module uses the sample" do
    assert sample() == [3, 1, 2]
  end
end

defmodule SecondTest do
  use ExUnit.Case
  import ListHelper

  test "second module uses the sample" do
    assert length(sample()) == 3
  end
end

result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")

Example

ExUnit.start(seed: 0, auto_run: false, formatters: [])

defmodule ListHelper do
  import ExUnit.Assertions

  def sample, do: [3, 1, 2]
  def sorted_sample, do: Enum.sort(sample())

  def check_sorted(list) do
    assert list == Enum.sort(list)
    length(list)
  end
end

defmodule SortedSampleTest do
  use ExUnit.Case
  import ListHelper

  test "sorted sample passes the shared check" do
    assert check_sorted(sorted_sample()) == 3
  end
end

defmodule OriginalSampleTest do
  use ExUnit.Case
  import ListHelper

  test "original sample stays unchanged" do
    assert sample() == [3, 1, 2]
    assert sorted_sample() == [1, 2, 3]
  end
end

result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")

Key points

  • Test helper: a shared module that holds setup and checks used by more than one test module.
  • Shared setup: one function that builds the same starting state for every test that needs it.
  • Shared check: one function that asserts the same result in several tests.
  • Import: a way to bring a helper's functions into a test module without copying them.
  • Single change: fixing the helper once fixes every test that uses it.

In this example, ListHelper supplies a sample list, a sorted version of it, and a shared sorted check. The first test module checks the sorted sample with the shared check. The second test module checks that the original sample is unchanged and that the sorted sample is in order.

Note

a helper holds test setup and checks, not the code being tested.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Why move shared setup into a test helper?