What is doctest?
doctest
is a macro that searches a specified module for code examples and automatically generates test cases.
Why use doctest?
Primarily to ensure the correctness of your module documentation. Doctest examples are also easy to write, can replace simple unit tests, and provide a standard format for specifying code examples and demonstrating your module's API.
Untested documentation trends toward incorrectness over time and eventually becomes worse than not having documentation at all.
A small example
# lib/small_math.ex
defmodule SmallMath do
@doc """
Adds two numbers together.
## Examples
iex> SmallMath.add(1, 2)
3
iex> SmallMath.add(5, 7)
23
"""
def add(a, b), do: a + b
end
# test/small_math_test.exs
defmodule SmallMathTest
use ExUnit.Case, async: true
doctest SmallMath
end
Output
$ mix test
Compiling 1 file (.ex)
.
1) doctest SmallMath.add/2 (2) (SmallMathTest)
test/small_math_test.exs:3
Doctest failed
code: SmallMath.add(5, 7) === 23
left: 12
stacktrace:
lib/small_math.ex:9: SmallMath (module)
Finished in 0.03 seconds
2 doctests, 1 failure
Randomized with seed 888764
As we see, doctest generated two test cases from our documentation, one of which failed.
How does it work?
doctest
looks for code examples by searching for lines beginning with iex>
and evaluates them as if it were a user sitting at the console. Afterward, a line that specifies a bare value (i.e. does not begin with iex>
or ...>
) is interpreted as a result, and a test is generated to assert that the value specified is what a user would see if they had evaluated the last expression.
@doc """
iex> 5 + 5 # expression
10 # result
"""
Multiline expressions
You can use ...>
to build a multiline expression.
@doc """
iex> add_one_fn = fn(number) ->
...> number + 1
...> end
iex> add_one_fn.(5)
6
"""
Testing for exceptions
Writing an example demonstrating an exception is also supported!
@doc """
iex> 1 + "hello"
** (ArithmeticError) bad argument in arithmetic expression
"""
Caveats
Tests generated by doctest
do not run in isolation of each other. Code that generates side effects is not a good match. Binding variables or defining code inside of a doctest also carries the risk that your code will be reused in another test.
Top comments (1)
Interesting note about side-effects in doctest code - I did not know that!