A library to test Erlang -moduledoc
and -doc
attributes.
OTP >= 27.
% rebar.config
{profiles, [
{test, [
{deps, [{doctest, "0.8.0"}]}
]}
]}.
Tests run via the doctest:module/1,2
function or on modules that include the doctest header.
The test code is defined like in the Erlang shell, starting the expression line from 1..N
and incrementing it by one for every test, and continues in multiple lines with ..
and aligning it with the code of the previous line, for example:
% Valid
1> foo.
foo
2> foo
.. =:=
.. bar.
bar
% Invalid
1> foo.
foo
200> foo % <- Must be 2 (previous line + 1)
.. =:= % <- Must be aligned
.. bar.
bar
Note Only exported functions are testable.
Take this module:
-module(foo).
-moduledoc """
Module doc tags can also be tested.
```erlang
1> foo:foo() =:= bar.
true
```
""".
-export([foo/0]).
-doc """
```erlang
1> foo:foo().
foo
```
""".
foo() ->
bar.
Running it via rebar3 as test shell
:
1> doctest:module(foo).
PASS ./test/support/foo.erl:6 -moduledoc
FAIL ./test/support/foo.erl:15 -doc
❌ assertEqual
Expected: foo
Received: bar
│
15 │ 1> foo:foo().
16 │ foo
│
└── at ./test/support/foo.erl:15
Tests: 1 failed, 1 passed, 2 total
Time: 0.007 seconds
Options can be provided when using the doctest:module/2
function. The available options are:
moduledoc
::boolean()
: enable or disable-moduledoc
testfuns
::boolean()
|[{atom(), arity()}]
: enable or disable-doc
tests or define the functions to be testedeunit
::resolve | term()
: set EUnit options
Note All functions are testable, exported, and non-exported.
Take this module:
-module(math).
-export([add/2]).
-ifdef(TEST).
-include_lib("doctest/include/doctest.hrl").
% -doctest <see the options section>.
-endif.
-doc """
Adds two numbers together.
_Example_:
```erlang
1> math:add(0, 1).
1
2> math:add(
.. 1,
.. 1
.. ).
2
```
""".
add(A, B) ->
A+B.
Now, by running rebar3 eunit
:
PASS ./src/math.erl:29 -doc
PASS ./src/math.erl:31 -doc
Tests: 2 passed, 2 total
Time: 0.008 seconds
By changing the first test to:
1> math:add(1, 1).
1
And running rebar3 eunit
again:
FAIL ./src/math.erl:15 -doc
❌ assertEqual
Expected: 1
Received: 2
│
15 │ 1> math:add(1, 1).
16 │ 1
│
└── at ./src/math.erl:15
PASS ./src/math.erl:17 -doc
Tests: 1 failed, 1 passed, 2 total
Time: 0.007 seconds
Options are defined via the -doctest
attribute and can be defined multiple times. The available options are:
boolean()
|{enabled, boolean()}
: enable or disable the test running, e.g.:-doctest true.
{moduledoc, boolean()}
: enable or disable-moduledoc
test, e.g.:-doctest {moduledoc, true}.
[{atom(), arity()}]
|{funs, [{atom(), arity()}] | boolean()}
: enable or disable-doc
tests or define the functions to be tested, e.g.:-doctest [add/2].
eunit
::resolve | term()
: set EUnit options, e.g.:-doctest {eunit, resolve}.
map()
: define all or partial options, e.g.:-doctest #{ enabled => true, moduledoc => true, funs => [add/2], eunit => resolve }.
Options can be globally defined via a config file, e.g.:
% config/sys.config
[{doctest, [
{enabled, true},
{moduledoc, false},
{funs, true},
{eunit, [no_tty, {report, {eunit_progress, [colored, profile]}}]}
]}].
Valid EUnit options are the resolve
atom and a proplist.
By defining resolve
as the EUnit options, doctest
will try to resolve the options via rebar
. Custom options can be defined as documented at rebar3.org:
The default EUnit options can be configured, as documented here.
Interesting undocumented options are:
no_tty
completely disables the default EUnit reporter output{report, {Module, Args}}
runs a custom EUnit reporter (the functionality that prints results to the shell). The reporter module needs the following callbacks implemented:-export([start/0]). -export([start/1]). -export([init/1]). -export([handle_begin/3]). -export([handle_end/3]). -export([handle_cancel/3]). -export([terminate/2]).
no_tty
andreport
can be combined to replace the EUnit reporter with a custom one:{eunit_opts, [no_tty, {report, {my_reporter, Opts}}]}.
doctest
has a built-in EUnit reporter called doctest_eunit_report
for better visualization of the tests. It can be used by defining the below option in the rebar.config
of your project:
{eunit_opts, [no_tty, {report, {doctest_eunit_report, []}}]}.
Currently, no options are expected/provided by the reporter.
An example of the doctest_eunit_report
output:
- More tests
- Specs
- Improve docs
If you like this tool, please consider sponsoring me. I'm thankful for your never-ending support ❤️
I also accept coffees ☕
Feel free to submit an issue on Github.
Copyright (c) 2024 William Fank Thomé
doctest
is 100% open source and community-driven. All components are available under the Apache 2 License on GitHub.
See LICENSE.md for more information.