Testing And Benchmarking in Elixir

Here’s the translation of the Go testing and benchmarking example to Elixir:

defmodule IntUtils do
  @moduledoc """
  This module contains utility functions for integer operations.
  """

  @doc """
  Returns the minimum of two integers.
  """
  def int_min(a, b) do
    if a < b, do: a, else: b
  end
end

defmodule IntUtilsTest do
  use ExUnit.Case
  doctest IntUtils

  test "basic int_min test" do
    assert IntUtils.int_min(2, -2) == -2
  end

  test "table driven tests" do
    test_cases = [
      {0, 1, 0},
      {1, 0, 0},
      {2, -2, -2},
      {0, -1, -1},
      {-1, 0, -1}
    ]

    for {a, b, expected} <- test_cases do
      assert IntUtils.int_min(a, b) == expected,
             "int_min(#{a}, #{b}) = #{IntUtils.int_min(a, b)}; want #{expected}"
    end
  end
end

defmodule IntUtilsBenchmark do
  use Benchfella

  bench "int_min" do
    IntUtils.int_min(1, 2)
  end
end

Unit testing is an important part of writing principled Elixir programs. The ExUnit module provides the tools we need to write unit tests, and the mix test command runs tests.

For the sake of demonstration, this code is in a single file, but it could be split into separate files. Testing code typically lives in a test directory, while the code being tested is in the lib directory.

We’ll be testing this simple implementation of an integer minimum. Typically, the code we’re testing would be in a source file named something like lib/int_utils.ex, and the test file for it would then be named test/int_utils_test.exs.

A test is created by writing a function with test macro in a module that uses ExUnit.Case.

We can use assert to check for equality and report test failures. If an assertion fails, the test will fail, but execution will continue to the next test.

Writing tests can be repetitive, so it’s idiomatic to use a table-driven style, where test inputs and expected outputs are listed in a list and a single loop walks over them and performs the test logic.

Benchmark tests in Elixir are typically created using a library like benchfella. The bench macro defines a benchmark, which will be run multiple times to get accurate measurements.

To run all tests in the current project in verbose mode:

$ mix test --trace

To run all benchmarks in the current project:

$ mix bench

Elixir’s testing and benchmarking ecosystem provides powerful tools for ensuring code correctness and performance. The ExUnit framework makes it easy to write and run tests, while libraries like benchfella allow for precise performance measurements.