OTP 27 has implemented the -doc
attribute, but what if its examples could be tested?
Thatβs the purpose of the doctest lib.
The motivation for it is this PR, which fixes some examples of the orddict
module.
Example
Take this module:
-module(math).
-moduledoc """
A module for basic arithmetic.
""".
-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.
The doctest/include/doctest.hrl
header contains a parse_transform
definition:
-compile({parse_transform, doctest_parse_transform}).
Including it and compiling via rebar3 as test compile
produces a file at `<project_cwd>/test/math_DOCTEST.erl with this content:
%%%---------------------------------------------------------------------
%%% Tests generated via doctest.
%%% - module: math
%%% - file: /home/williamthome/Projects/erlang/doctest_example/src/math.erl
%%%---------------------------------------------------------------------
-module(math_DOCTEST).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
add_2_test() ->
[
?assertEqual(math:add(0, 1), 1),
?assertEqual(math:add( 1, 1 ), 2)
].
-endif.
Then by running rebar3 eunit -v
:
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling doctest_example
===> Performing EUnit tests...
======================== EUnit ========================
file "doctest_example.app"
application 'doctest_example'
module 'math'
math_DOCTEST: add_2_test (module 'math_DOCTEST')...ok
=======================================================
Test passed.
For more examples, please see the test module and the auto-generated test file.
Options
Some options can be provided via -doctest
attribute, e.g.:
boolean()
: enable or disable tests.-doctest true.
atom()
: define the test module name.-doctest math_DOCTEST.
proplists:proplist()
|all
: define the functions to be tested.-doctest [add/2].
string()
|{abs, string()}
|{cwd, string()}
: define the test file location.-doctest {abs, "/tmp/doctests"}.
map()
: define all or partial options.-doctest #{ enabled => true, module => math_DOCTEST, funs => [add/2], location => {abs, "/tmp/doctests"} }.
Requirements
doctest
requires OTP 27 or higher.
Installation
% rebar.config
{profiles, [
{test, [
{deps, [{doctest, "0.1.0"}]}
]}
]}.
TODO
- More tests;
- Ability to test modules via function (maybe also escript) and not only via
parse_transform
, e.g.:doctest:module(math, _Opts = #{}).
- Maybe add a mechanism to test non-exported functions, but probably this makes no sense;
- Test on umbrella applications;
- Improve documentation;
- Implement a
-moduledoc
test like for-doc
by adding a test function calledmoduledoc_test/0
; - Add unbound variables support, e.g.:
1> Foo = foo. foo; 2> foo =:= Foo. true
I have no doubt that there are bugs. Just a few tests have been implemented.
My idea is to collect suggestions and know what you think about it.
So, suggestions are welcome