sb logoToday I Learned

2 posts by rudolfmanusadzhian @RudManusachi

New line between examples in doctests matters.

The following module with doctest generates 1 test and makes 2 assertions in it.

defmodule Foo do
  @doc """
  Does foo

  ## Examples

      iex> Foo.bar(1)
      1
      iex> Foo.bar(2)
      2
  """
  def bar(a), do: a
end

defmodule FooTest do
  use ExUnit.Case, async: true
  doctest Foo
end

Roughly an equivalent of:

defmodule FooTest do
  use ExUnit.Case, async: true

  test "Foo.bar/1" do
    assert Foo.bar(1) === 1
    assert Foo.bar(2) === 2
  end
end

However, if we intersperse the examples with new lines

defmodule Foo do
  @doc """
  Does foo

  ## Examples

      iex> Foo.bar(1)
      1

      iex> Foo.bar(2)
      2
  """
  def bar(a), do: a
end

Doctest would generate 2 tests with single assertion in each.

Thus, when we run mix test the number of doctests differs.

That could also be verified by calling in iex -S mix an undocumented function ExUnit.DocTest.__doctests__/2 which returns the list of generated ASTs for tests.

iex> ExUnit.DocTest.__doctests__(Foo, only: [bar: 1]) |> Enum.map(&Macro.to_string/1) |> IO.puts()

{"Foo.bar/1 (1)",
 (
   value = Foo.bar(1)
   expected = 1
   formatted = "iex> Foo.bar(1)"
   last_expr = "Foo.bar(1)"
   expected_expr = "1"
   stack = [{Foo, :__MODULE__, 0, line: 7, file: "lib/foo.ex"}]
   ExUnit.DocTest.__test__(value, expected, formatted, last_expr, expected_expr, stack)
 )}
{"Foo.bar/1 (2)",
 ...