Testing with ExUnit

Assertions: assert, refute, assert_raise, assert_in_delta, assert_receive, assert_received, refute_receive, catch_throw, catch_exit, flunk.

27.1 Basics

Assertions: assert, refute, assert_raise, assert_in_delta, assert_receive, assert_received, refute_receive, catch_throw, catch_exit, flunk. Pattern assertions: assert {:ok, %{id: id}} = create().

# test/math_test.exs
defmodule MathTest do
  use ExUnit.Case, async: true
  doctest Math

  test "sum/2 adds numbers" do
    assert Math.sum(1, 2) == 3
  end

  test "raises on bad input" do
    assert_raise ArithmeticError, fn -> Math.sum(1, :a) end
  end

  describe "square/1" do
    test "positive" do
      assert Math.square(3) == 9
      refute Math.square(3) == 10
    end
  end
end

27.2 Setup and context

setup do
  user = %User{name: "Ann"}
  {:ok, user: user}
end

setup :create_account            # named setup function
setup_all do ... end             # once per module

test "uses context", %{user: user} do
  assert user.name == "Ann"
end

on_exit(fn -> cleanup() end)

27.3 Tags & filtering

test/test_helper.exs: ExUnit.start(exclude: [:integration]).

@tag :slow
@tag timeout: 120_000
@moduletag :integration
@tag :skip
mix test --only slow
mix test --exclude integration

27.4 Doctests

@doc """
Adds two numbers.

    iex> Math.sum(1, 2)
    3
"""
def sum(a, b), do: a + b

27.5 Testing processes

test "receives message" do
  send(self(), {:hello, 1})
  assert_receive {:hello, n} when n > 0, 500
end

test "genserver" do
  pid = start_supervised!({Counter, 0})
  Counter.increment(pid)
  assert Counter.value(pid) == 1
end

27.6 Mocks with Mox

# define a behaviour, then in test_helper.exs:
Mox.defmock(MyApp.HTTPMock, for: MyApp.HTTPClient)

# in test:
import Mox
setup :verify_on_exit!
test "fetches" do
  expect(MyApp.HTTPMock, :get, fn "url" -> {:ok, "body"} end)
  assert MyApp.fetch("url") == {:ok, "body"}
end

27.7 Property-based testing

use ExUnitProperties   # stream_data
property "reverse twice is identity" do
  check all list <- list_of(integer()) do
    assert list |> Enum.reverse() |> Enum.reverse() == list
  end
end

27.8 Coverage & parameterised tests

mix test --cover; excoveralls for reports. 1.18+ supports parameterize: on ExUnit.Case to run a module with different parameters.