查看源码 Common Test 的属性测试支持: ct_property_test

概述

Common Test 属性测试支持 (ct_property_test) 是一种辅助工具,用于在 Common Test 测试套件中运行基于属性的测试工具。

以下内容假设您已具备基于属性的测试的基本知识。同时假设您的库路径中已安装并可用以下至少一种基于属性的测试工具:

支持什么?

ct_property_test 模块执行以下操作:

  • 编译子目录 property_test 中包含属性测试的文件。
  • 使用第一个找到的属性测试工具测试这些文件中的属性。
  • 将结果(即打印输出)保存在通常的 Common Test 日志中。

入门示例

假设我们想测试 lists:sort/1 函数。

我们需要一个属性来测试该函数。按照通常的方法,我们在应用程序的 test 目录中创建 property_test/ct_prop.erl 模块。

-module(ct_prop).
-export([prop_sort/0]).

%%% This will include the .hrl file for the installed testing tool:
-include_lib("common_test/include/ct_property_test.hrl").

%%% The property we want to check:
%%%   For all possibly unsorted lists,
%%%   the result of lists:sort/1 is sorted.
prop_sort() ->
    ?FORALL(UnSorted, list(),
            is_sorted(lists:sort(UnSorted))
           ).

%%% Function to check that a list is sorted:
is_sorted([]) ->
    true;
is_sorted([_]) ->
    true;
is_sorted([H1,H2|SortedTail]) when H1 =< H2 ->
    is_sorted([H2|SortedTail]);
is_sorted(_) ->
    false.

我们还需要一个 CommonTest 测试套件。

-module(ct_property_test_SUITE).
-compile(export_all). % Only in tests!

-include_lib("common_test/include/ct.hrl").

all() -> [prop_sort
         ].

%%% First prepare Config and compile the property tests for the found tool:
init_per_suite(Config) ->
    ct_property_test:init_per_suite(Config).

end_per_suite(Config) ->
    Config.

%%%================================================================
%%% Test suites
%%%
prop_sort(Config) ->
    ct_property_test:quickcheck(
      ct_prop:prop_sort(),
      Config
     ).

我们像往常一样运行它,例如在 OS shell 中使用 ct_run。

..../test$ ct_run -suite ct_property_test_SUITE
.....
Common Test: Running make in test directories...

TEST INFO: 1 test(s), 1 case(s) in 1 suite(s)

Testing lib.common_test.ct_property_test_SUITE: Starting test, 1 test cases

----------------------------------------------------
2019-12-18 10:44:46.293
Found property tester proper
at "/home/X/lib/proper/ebin/proper.beam"


----------------------------------------------------
2019-12-18 10:44:46.294
Compiling in "/home/..../test/property_test"
  Deleted:   ["ct_prop.beam"]
  ErlFiles:  ["ct_prop.erl"]
  MacroDefs: [{d,'PROPER'}]

Testing lib.common_test.ct_property_test_SUITE: TEST COMPLETE, 1 ok, 0 failed of 1 test cases

....

一个有状态测试的例子

假设有一个测试会生成一些并行的有状态命令,并运行 300 个测试。

prop_parallel(Config) ->
    numtests(300,
             ?FORALL(Cmds, parallel_commands(?MODULE),
                     begin
                         RunResult = run_parallel_commands(?MODULE, Cmds),
                         ct_property_test:present_result(?MODULE, Cmds, RunResult, Config)
                     end)).

ct_property_test:present_result/4 是一个辅助函数,用于在 CommonTest 日志文件中打印一些统计信息。

我们的示例测试可以例如是对一个 ftp 服务器的简单测试,我们在其中执行 get、put 和 delete 请求,其中一些是并行的。默认情况下,结果有三个部分:

*** User 2019-12-11 13:28:17.504 ***

Distribution sequential/parallel

 57.7% sequential
 28.0% parallel_2
 14.3% parallel_1



*** User 2019-12-11 13:28:17.505 ***

Function calls

 44.4% get
 39.3% put
 16.3% delete



*** User 2019-12-11 13:28:17.505 ***

Length of command sequences

Range  : Number in range
-------:----------------
 0 -  4:    8    2.7%  <-- min=3
 5 -  9:   44   14.7%
10 - 14:   74   24.7%
15 - 19:   60   20.0%  <-- mean=18.7 <-- median=16.0
20 - 24:   38   12.7%
25 - 29:   26    8.7%
30 - 34:   19    6.3%
35 - 39:   19    6.3%
40 - 44:    8    2.7%
45 - 49:    4    1.3%  <-- max=47
        ------
          300

第一部分 - 分布 sequential/parallel - 显示 parallel_commands/1 结果的顺序和并行部分中的分布。有关此函数的说明,请参阅任何属性测试工具。该表格显示,在所有命令(在本例中为 get 和 put)中,57.7% 在并行部分之前按顺序执行,28.0% 在第一个并行列表中执行,其余的在第二个并行列表中执行。

第二部分 - 函数调用 - 显示生成的命令列表中三个调用的分布。我们看到所有三个调用都已执行。如果假设我们还生成了第四个调用,那么这样的表格会显示我们失败了。

第三部分也是最后一部分 - 命令序列的长度 - 显示生成的命令序列的统计信息。我们看到最短的列表有三个元素,而最长的列表有 47 个元素。还显示了平均值和中位数。此外,例如,我们可以看到只有 2.7% 的列表(即八个列表)只有三个或四个元素。