为什么 Eunit 不需要导出测试函数?
Why does Eunit not require test functions to be exported?
我正在阅读 Learn You Some Erlang 中的 EUnit 一章,我从所有代码示例中注意到的一件事是测试函数从未在 -export()
子句中声明。
为什么 EUnit 能够提取这些测试函数?
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 module
Causes all functions whose names match ..._test()
or ..._test_()
to be automatically exported from the module (unless testing is turned off, or the EUNIT_NOAUTO
macro is defined)
很高兴我发现了这个问题,因为它给了我一种有意义的拖延方式,我想知道函数是如何动态创建和导出的。
首先查看 Erlang/OTP Github 存储库中影响 EUnit 的最新提交,即 4273cbd。 (这样做的唯一原因是为了找到一个相对稳定的锚而不是git分支。)
0。包含EUnit的头文件
根据 EUnit's User's Guide,第一步是在测试模块中 -include_lib("eunit/include/eunit.hrl").
,所以我认为这就是奇迹发生的地方。
1。 otp/lib/eunit/include/eunit.hrl
(第 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.
1.1 -compile({parse_transform, eunit_autoexport}).
是什么意思?
来自 Erlang 参考手册的模块章节 (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.
到 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.
来自 erl_id_trans
模块:
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 function parse_transform/2
is called by
the compiler before the code is checked for errors.
基本上,如果模块 M 包含 {parse_transform, Module}
编译选项,那么 M 的所有函数和属性都可以通过使用 Module:parse_transform/2
的实现进行迭代。它的第一个参数是Forms
,也就是Erlang的abstract format (described in Erlang Run-Time System Application (ERTS) User's Guide.
中描述的M的模块声明
2。 otp/lib/eunit/src/eunit_autoexport.erl
此模块仅导出 parse_transfrom/2
以满足 {parse_transform, Module}
编译选项,其首要任务是找出测试用例函数和生成器的配置后缀是什么。如果不是手动设置,则分别使用 _test
和 _test_
(通过 lib/eunit/src/eunit_internal.hrl
)。
然后它使用 eunit_autoexport:form/5
扫描模块的所有函数和属性,并构建一个要导出的函数列表,其中上面的后缀匹配(加上原始函数。我在这个上可能是错的...).
最后,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。
我正在阅读 Learn You Some Erlang 中的 EUnit 一章,我从所有代码示例中注意到的一件事是测试函数从未在 -export()
子句中声明。
为什么 EUnit 能够提取这些测试函数?
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)
很高兴我发现了这个问题,因为它给了我一种有意义的拖延方式,我想知道函数是如何动态创建和导出的。
首先查看 Erlang/OTP Github 存储库中影响 EUnit 的最新提交,即 4273cbd。 (这样做的唯一原因是为了找到一个相对稳定的锚而不是git分支。)
0。包含EUnit的头文件
根据 EUnit's User's Guide,第一步是在测试模块中 -include_lib("eunit/include/eunit.hrl").
,所以我认为这就是奇迹发生的地方。
1。 otp/lib/eunit/include/eunit.hrl
(第 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.
1.1 -compile({parse_transform, eunit_autoexport}).
是什么意思?
来自 Erlang 参考手册的模块章节 (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.
到 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.
来自 erl_id_trans
模块:
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.
基本上,如果模块 M 包含 {parse_transform, Module}
编译选项,那么 M 的所有函数和属性都可以通过使用 Module:parse_transform/2
的实现进行迭代。它的第一个参数是Forms
,也就是Erlang的abstract format (described in Erlang Run-Time System Application (ERTS) User's Guide.
2。 otp/lib/eunit/src/eunit_autoexport.erl
此模块仅导出 parse_transfrom/2
以满足 {parse_transform, Module}
编译选项,其首要任务是找出测试用例函数和生成器的配置后缀是什么。如果不是手动设置,则分别使用 _test
和 _test_
(通过 lib/eunit/src/eunit_internal.hrl
)。
然后它使用 eunit_autoexport:form/5
扫描模块的所有函数和属性,并构建一个要导出的函数列表,其中上面的后缀匹配(加上原始函数。我在这个上可能是错的...).
最后,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。