🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Test Organization

Test organization is grouping related tests and marking them so a run stays easy to read. Groups and marks make a failure point at one part of the code.

Groups keep related tests together

Two describe blocks group one addition test and one subtraction test. The run counts both tests.

ExUnit.start(seed: 0, auto_run: false, formatters: [])
defmodule MathTest do
  use ExUnit.Case

  describe "addition" do
    test "adds two numbers" do
      assert 1 + 2 == 3
    end
  end

  describe "subtraction" do
    test "subtracts two numbers" do
      assert 5 - 2 == 3
    end
  end
end
result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")

Marks label each test

Each test carries a mark naming its area. Marks do not change the run, so both marked tests pass.

ExUnit.start(seed: 0, auto_run: false, formatters: [])
defmodule MarkedTest do
  use ExUnit.Case

  @tag area: :math
  test "adds" do
    assert 2 + 3 == 5
  end

  @tag area: :words
  test "upcases" do
    assert String.upcase("ana") == "ANA"
  end
end
result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")

A run can leave marked tests out

This run excludes the slow mark. The total still counts the left-out test, and the excluded count names it.

ExUnit.start(seed: 0, auto_run: false, formatters: [], exclude: [slow: true])
defmodule MarkedTest do
  use ExUnit.Case

  @tag slow: true
  test "slow one" do
    assert 1 == 1
  end

  test "fast one" do
    assert 2 == 2
  end
end
result = ExUnit.run()
IO.puts("total: #{result.total}")
IO.puts("excluded: #{result.excluded}")
IO.puts("failures: #{result.failures}")

Example

ExUnit.start(seed: 0, auto_run: false, formatters: [])
defmodule CalcTest do
  use ExUnit.Case

  describe "addition" do
    @tag area: :math
    test "adds" do
      assert 2 + 3 == 5
    end

    test "adds zero" do
      assert 5 + 0 == 5
    end
  end

  describe "upcasing" do
    test "upcases a word" do
      assert String.upcase("ana") == "ANA"
    end
  end
end
result = ExUnit.run()
IO.puts("passed: #{result.total - result.failures}/#{result.total}")

Key points

  • Describe: a describe block groups tests about one thing under one name.
  • Tag: a tag marks a test, so a run can include or exclude marked tests.
  • Layout: a test file starts ExUnit, then defines one test module, then runs and prints counts.
  • One part: each describe block covers one part, so a failure points at one area.
  • Marking: a tag sits above the test it marks, written as a module attribute line.

In this example, one test module holds two describe blocks, one for addition with two tests and one for upcasing with one test. One addition test also carries an area mark, and all three tests pass.

Note

tags are labels only, so a marked test still runs unless the run excludes its tag.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What is a describe block for?