Reputation: 27803
I'm going through the EUnit chapter in Learn You Some Erlang and one thing I am noticing from all the code samples is the test functions are never declared in -export()
clauses.
Why is EUnit able to pick these test functions up?
Upvotes: 3
Views: 404
Reputation: 8273
Glad I found this question because it gives me a meaningful way to procrastinate and I was wondering how functions get created and exported dynamically.
Started by looking at the latest commit affecting EUnit in the Erlang/OTP Github repo, which is 4273cbd. (The only reason for this was to find a relatively stable anchor instead of git branches.)
According EUnit's User's Guide, the first step is to -include_lib("eunit/include/eunit.hrl").
in the tested module, so I assume this is where the magic happens.
otp/lib/eunit/include/eunit.hrl
(lines 79 - 91)%% Parse transforms for automatic exporting/stripping of test functions.
%% (Note that although automatic stripping is convenient, it will make
%% the code dependent on this header file and the eunit_striptests
%% module for compilation, even when testing is switched off! Using
%% -ifdef(EUNIT) around all test code makes the program more portable.)
-ifndef(EUNIT_NOAUTO).
-ifndef(NOTEST).
-compile({parse_transform, eunit_autoexport}).
-else.
-compile({parse_transform, eunit_striptests}).
-endif.
-endif.
-compile({parse_transform, eunit_autoexport}).
mean?From the Erlang Reference Manual's Module chapter (Pre-Defined Module Attributes):
-compile(Options).
Compiler options. Options is a single option or a list of options. This attribute is added to the option list when compiling the module. See the compile(3) manual page in Compiler.
On to compile(3):
{parse_transform,Module}
Causes the parse transformation function Module:parse_transform/2 to be applied to the parsed code before the code is checked for errors.
From the erl_id_trans
module:
This module performs an identity parse transformation of Erlang code. It is included as an example for users who wants to write their own parse transformers. If option
{parse_transform,Module}
is passed to the compiler, a user-written functionparse_transform/2
is called by the compiler before the code is checked for errors.
Basically, if module M includes the {parse_transform, Module}
compile option, then all of M's functions and attributes can be iterated through using your implementation of Module:parse_transform/2
. Its first argument is Forms
, which is M's module declaration described in Erlang's abstract format (described in Erlang Run-Time System Application (ERTS) User's Guide.
otp/lib/eunit/src/eunit_autoexport.erl
This module only exports parse_transfrom/2
to satisfy {parse_transform, Module}
compile option and its first order of business is to figure out what are the configured suffixes for test case functions and generators. If not set manually, using _test
and _test_
respectively (via lib/eunit/src/eunit_internal.hrl
).
It then scans all the functions and attributes of your module using eunit_autoexport:form/5
, and builds a list of to be exported functions where the suffixes above match (plus the original functions. I may be wrong on this one...).
Finally, eunit_autoexport:rewrite/2
builds a module declaration from the original Forms
(given to eunit_autoexport:parse_transform/2
as the first argument) and the list of functions to be exported (that was supplied by form/5
above). On line 82 it injects the test/0
function mentioned in the EUnit documentation.
Upvotes: 0
Reputation: 8340
The simplest way to use EUnit in an Erlang module is to add the following line at the beginning of the module (after the
-module
declaration, but before any function definitions):-include_lib("eunit/include/eunit.hrl").
This will have the following effect:
Creates an exported function
test()
(unless testing is turned off, and the module does not already contain a test() function), that can be used to run all the unit tests defined in the moduleCauses all functions whose names match
..._test()
or..._test_()
to be automatically exported from the module (unless testing is turned off, or theEUNIT_NOAUTO
macro is defined)
Upvotes: 3