《Erlang Test》

《Erlang Test》

1
前言:格物致知

目录:

  1. common_test使用笔记
  2. eunit使用笔记

    一、common_test

    (1)初识common_test

    1.common_test 简介
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Automated execution of test suites (sets of test cases)
    自动执行测试的用例
    Logging of events during execution
    在执行期间记录事件
    HTML presentation of test suite results

    HTML presentation of test suite code
    Support functions for test suite authors
    Step-by-step execution of test cases
    2.common_test 基本用法
    一些通用的概念
    1
    2
    3
    必须导出 ----- all/0
    命名规则 ----- *_SUITE.erl
    推荐导入 ----- -include(ct.hrl)

测试规范语法(test.spec)

1
2
3
4
5
6
7
8
9
{Key, value}
解释一些通用的方法
include -> 导入一些路径,不只是hrl
ct_hooks -> 安装common_test的一些插件
suites -> 测试用例
skip_suites -> 跳过测试用例
skip_groups -> 跳过测试用例集合
config -> 配置文件路径
alias -> 别名,方便配置(新版Erlang推荐使用define )

config写法

1
2
3
4
写:
{Key,value}. **注意.号
读:
ct:get_config(Key).

启动

1
2
3
4
5
6
Command line内
ct_run -spec test.spec -pa ../ebin -noinput -sname XXX

Erlang shell内
ct:run_test([{suite,"./my_SUITE"},{logdir,"./results"}]).
`
  • 引用

1.更多规范阅读官方文档
地址 https://erlang.org/doc/apps/common_test/run_test_chapter.html

(2)Common_test实践

  • 通用导出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    all()->
    [
    %%假设写了一个背包模块需要测试
    t_bag_function/1 %%必须给一个参数用来装Config
    ].

    %%全局的一个CTH的配置,后面会讲CTH的作用
    suite() ->
    [{ct_hooks, [cth_surefire]}, {timetrap, {seconds, 30}}].
    1.testcase模式(这里我理解为单例,一个test进程做事
  • 导出

    1
    2
    3
    4
    5
    6
    7
    8
    init_per_testcase(TestCase, Config) ->  
    Link = LinkSupport:start_link(),
    [{link, Link} | Config].


    end_per_testcase(TestCase, Config) ->
    ok.
    %%可以在这里做一些事情,比如说创建连接
  • t_bag_function/1的函数

    1
    2
    3
    4
    5
    t_bag_function(Config) ->
    Link = ?config(link, Config),
    R = #get_bag_item{},
    #bag_item{} = LinkSupport:sendandwait(Clinet ,R).
    %%阻塞在接受消息,会有自己可以做Socket的超时限制,CTH也有超时限制,每个用例不超过30s
2.group模式

我理解为,一组行为组模式

  • 导出
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    groups() ->
    [
    {groupname, prop, testcase}
    ].

    %% testcases = [testcase], testcase = 单例|group
    %% prop = [repeat, mode]
    %% mode = 并发|顺序|随机, repeat有很多,可以看看官方文档的返回值


    init_per_group(_Groupname, Config) ->
    Link = LinkSupport:start_link(),
    [{link, Link} | Config].

    end_per_group(_Groupname, _Config) ->
    ok.
  • 遇到的问题
  1. 假设把创建连接,放到per_group中,等创建testcase再拿出来的时候会报错,假设连接时TCP,因为此时的group进程会挂掉,Socket Controlling是group 然后当testcase进程需要访问Socket的时候,会报错。
  2. 每个组内的用例,都是独立的,都是独立的进程,每次启动都只会Goup传过来的Conifg(待考就,是否有配置可调,但是我没测出来可以)
  • 实现t_bag_function/1的函数

更多方法function参考
地址 https://erlang.org/doc/man/common_test.html

–总结–

假设需要多例测试的话,在group内用test_case即可,然后再每个test_case的用例方法抽出来,需要用哪个就调用哪个

(3)Common_test拓展

1.CTH

待补充


二、eunit

初始Eunit

1
2
官方文档提到,参考了很多oop unit的思想,
一般来说,可以测试模块、进程、甚至整个应用,但是unit这个词代表单元,最好还是以模块为基准去测试

Eunit实践

step1:包含库

1
-include_lib("eunit/include/eunit.hrl").

step2:导出test方法

1
2
3
4
5
6
7
1.官方文档说到任何匹配..._test() or ..._test_的函数,全部都会自动的被认为是导出的,
2.单元测试时,用期望值去匹配函数,例如:[2,1] = lists:reverse([1,2]). 假设期望值不正确就抛错误
3.并且函数结尾不要catch错误然后返回返回值,让其直接抛出错误让Eunit catch ,任何返回值返回,Eunit都会当时成功

%%test generator
命名规则:”_test_"
返回一个测试集合,免掉了每个测试用例都要写一个单独的方法

step3:一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  -module(fib).
-export([fib/1]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

fib(0) -> 1;
fib(1) -> 1;
fib(N) when N > 1 -> fib(N-1) + fib(N-2).

fib_test_() ->
[?_assert(fib(0) =:= 1),
?_assert(fib(1) =:= 1),
?_assert(fib(2) =:= 2),
?_assert(fib(3) =:= 3),
?_assert(fib(4) =:= 5),
?_assert(fib(5) =:= 8),
?_assertException(error, function_clause, fib(-1)),
?_assert(fib(31) =:= 2178309)
].
  • 关于宏定义的使用
    1
    2
    3
    4
    5
    6
    ?assert(表达式) %%判断返回值是否是true,不是就抛错
    ?_test(表达式) %%自动添加行号当测试进行的时候,增加可读性
    ?_assertException(表达式) %%

    %%定义不要测试的宏
    -define(NOTEST, 1).

更多宏的使用请参考官方文档: https://erlang.org/doc/apps/eunit/chapter.html#Simple_test_objects