Erlang单元测试框架
EUnit头文件
-include_lib("eunit/include/eunit.hrl").
在Erlang模块中使用EUnit,需要在模块中包含EUnit头文件(位于模块声明之后,函数声明之前)。
在xx.erl
模块中声明了EUnit头文件会产生3种影响:
- 自动导出一个
xx:test/0
函数 - 自动导出xx模块中全部函数名以
_test/0
和_test_/0
结尾的函数 - 可以在xx模块中使用EUnit的宏定义
为了使EUnit头文件生效,Erlang的模块搜索路径必须包含一个以eunit/ebin
结尾的目录(指向EUnit安装目录的ebin子目录)。如果EUnit作为lib/eunit
安装在Erlang/OTP系统目录下,那么当Erlang启动时,EUnit的ebin子目录会自动添加到搜索路径;否则,就需要通过向erl
或erlc
命令传递-pa
参数来显式的添加目录:
e.g.erlc -pa "path/to/eunit/ebin" $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
如果希望EUnit始终可用,则可以在$HOME/.erlang
文件中添加一行code:add_path("/path/to/eunit/ebin").
_test/0
一个名称以_test/0
结尾的函数会被EUnit识别成一个简单的测试函数:
测试函数返回任意值都会被EUnit丢弃,会被认为测试通过;
如果测试函数抛出异常,或者死循环(这种情况下它会在一段时间后被终止),会被认为测试未通过。
1 | reverse_list(List) -> |
Running EUnit
当xx.erl
包含了EUnit头文件,并且添加了测试代码后,可以在Erlang shell中使用xx:test().
或者eunit:test(xx).
运行测试代码。
并行执行eunit:test({inparallel, xx}).
将测试代码放在单独模块中
为xx.erl
编写测试模块xx_tests.erl
(NOTxx_test.erl
),当请求EUnit对xx模块进行测试,它也会寻找xx_tests模块。
EUnit捕获标准输出
EUnit会捕获测试代码中全部的标准输出,在测试代码中产生标准输出,不会出现在Erlang shell中
如果要在测试代码中产生标准输出,可以用使用io:format(user, "~w", [X])
写入用户输出流,或者使用EUnit宏
_test_/0
_test/0
的缺点是必须为每一个测试用例编写一个独立的函数(还得具有独立的名字),而使用_test_/0
可以编写返回测试的函数(test generator function)。
1 | basic_test_() -> |
事实上,EUnit处理所有的简单测试_test/0
,就像处理fun表达式一样,将它们放在一个列表中,并逐个运行它们。
1 | %% _test宏 |
?_assert()
是?_test(?assert())
的简写。
An example
1 | -module(fib). |
禁用测试
EUnit可以通过在编译时定义NOTEST
宏来关闭erlc -DNOTEST xx.erl
或者在包含EUnit头文件之前添加宏定义-define(NOTEST, 1).
该值不重要,通常是1或者true
除非定义了EUNIT_NOAUTO
宏,否则NOTEST
宏会自动将代码中没有明确声明导出的测试函数删除
采用条件编译来避免EUnit对编译的依赖
1 | -ifdef(TEST). |