10. Code Coverage Analysis
10代码覆盖率分析
10.1总则
虽然Common Test
主要是为了黑盒测试而创建的,但也没有什么能阻止它作为一个白盒测试工具完美工作。当测试应用程序用Erlang编写时尤其如此。然后用Erlang函数调用很容易实现测试端口。
当使用白盒测试Erlang应用程序时,能够测量测试的代码覆盖率非常有用。Common Test
为此提供了对OTP封面工具的简单访问。Common Test
处理与Cover工具的所有必要通信(启动,编译,分析等)。该Common Test
用户只需要指定代码覆盖分析的程度。
10.2使用
要指定要包含在代码覆盖率测试中的模块,请提供封面规范文件。通过这个文件,您可以指出特定的模块或指定包含要包含在分析中的模块的目录。您还可以指定要从分析中排除的模块。
如果您正在测试分布式Erlang应用程序,那么代码覆盖率分析中包含的代码很可能会在另一个Erlang节点Common Test
上运行,而不是运行的代码。如果是这样,则必须在封面规范文件中指定这些其他节点,或将它们动态添加到节点的代码覆盖集。有关后者的详细信息,请参阅模块ct_cover
。
在封面规范文件中,您还可以指定所需的代码覆盖率分析级别;details
或overview
在详细模式下,您将得到一个覆盖概述页面,显示每个模块和总覆盖率百分比。您还可以为分析中包含的每个模块打印一个HTML文件,显示在测试期间执行了哪些部分的代码。在概述模式中,只打印代码覆盖率概述页。
您可以选择在测试之间导出和导入代码覆盖率数据。如果您在封面规范文件中指定了导出文件的名称Common Test
,则在测试结束时将收集的封面数据导出到此文件。您可以类似地指定先前导出的数据以便导入并包含在分析中以进行测试(可以指定多个导入文件)。这样,可以分析总代码覆盖率,而不必一次运行所有测试。
要激活代码覆盖支持,请在启动时指定封面规范文件的名称Common Test
。通过使用标志做这个-cover
有ct_run
,例如:
$ ct_run -dir $TESTOBJS/db -cover $TESTOBJS/db/config/db.coverspec
通过向参数ct:run_test/1
添加{cover,CoverSpec}
元组,您还可以在调用中传递封面规范文件名Opts
。
您也可以在测试规范中启用代码覆盖(请参阅Test Specifications
运行测试和分析结果一节中的部分)。
10.3当测试完成时停止盖子工具
默认情况下,测试完成后封面工具会自动停止。这会导致原始(未覆盖编译的)模块被加载回测试节点。如果此时的进程仍然运行覆盖编译的任何模块的旧代码,这意味着它在封装编译后没有完成任何完全限定的函数调用,则会终止该进程。为避免这种情况,请将选项的值设置cover_stop
为false
。这意味着模块保持封面编译。因此,只有在测试完成后终止测试中的Erlang节点,或者可以手动停止覆盖时,才推荐这样做。
选项可以通过使用标志被设置-cover_stop
以ct_run
,通过添加{cover_stop,true|false}
到参数Opts
到ct:run_test/1
,或通过添加cover_stop
在测试规范中,术语(见节Test Specifications
在部分运行测试和分析结果)。
10.4封面规格文件
在封面规范文件中允许使用下列术语:
%% List of Nodes on which cover will be active during test.
%% Nodes = [atom()]
{nodes, Nodes}.
%% Files with previously exported cover data to include in analysis.
%% CoverDataFiles = [string()]
{import, CoverDataFiles}.
%% Cover data file to export from this session.
%% CoverDataFile = string()
{export, CoverDataFile}.
%% Cover analysis level.
%% Level = details | overview
{level, Level}.
%% Directories to include in cover.
%% Dirs = [string()]
{incl_dirs, Dirs}.
%% Directories, including subdirectories, to include.
{incl_dirs_r, Dirs}.
%% Specific modules to include in cover.
%% Mods = [atom()]
{incl_mods, Mods}.
%% Directories to exclude in cover.
{excl_dirs, Dirs}.
%% Directories, including subdirectories, to exclude.
{excl_dirs_r, Dirs}.
%% Specific modules to exclude in cover.
{excl_mods, Mods}.
%% Cross cover compilation
%% Tag = atom(), an identifier for a test run
%% Mod = [atom()], modules to compile for accumulated analysis
{cross,[{Tag,Mods}]}.
术语incl_dirs_r
并excl_dirs_r
指示Common Test
递归搜索指定的目录,并包含或排除搜索期间找到的任何模块。术语incl_dirs
和excl_dirs
结果是对模块的非递归搜索(即只包含或排除在指定目录中找到的模块)。
注
包含在代码覆盖率测试中的Erlang模块的目录必须存在于代码服务器路径中。否则,封面工具无法重新编译模块。在封面规范文件中指定这些目录是不够的Common Test
。
10.5十字封面分析
交叉覆盖机制允许在多个测试中对模块进行覆盖分析。如果某些代码(例如库模块)被许多不同的测试使用并且累积的覆盖结果是可取的,则这很有用。
这也可以通过使用参数,以更定制的方式实现。export
在封面规范和分析结果离线。然而,交叉覆盖机制是一个内置的解决方案,也提供日志记录。
该机制最简单的解释是通过一个例子:
假设有两个系统,s1
并且s2
在单独的测试运行中进行了测试。系统s1
包含一个m1
通过测试运行测试的库模块,s1
并包含在封面规范中s1
,如下所示:
s1.cover:
{incl_mods,[m1]}.
在分析代码覆盖率时,m1
可以在s1
测试结果的封面日志中看到结果。
现在,想象一下,作为m1
一个库模块,它也经常被系统使用s2
。测试运行s2
没有特别的测试m1
,但看看测试m1
覆盖了哪些部分仍然很有趣s2
。要做到这一点,m1
也可以包括在封面规格s2
如下:
s2.cover:
{incl_mods,[m1]}.
这m1
也为测试运行的封面日志提供了条目s2
。问题是,这仅反映了覆盖s2
测试,而不是在累加结果s1
和s2
。这是交叉覆盖机制派上用场的地方。
如果封面规格s2
如下所示:
s2.cover:
{cross,[{s1,[m1]}]}.
然后m1
是在测试运行中编译的封面s2
,但未在覆盖范围日志中显示。相反,如果ct_cover:cross_cover_analyse/2
在两者都完成s1
并且s2
测试运行完成后调用,则累积结果m1
可在交叉封面日志中进行测试运行s1
。
对分析函数的调用必须如下所示:
ct_cover:cross_cover_analyse(Level, [{s1,S1LogDir},{s2,S2LogDir}]).
在这里,S1LogDir并S2LogDir在指定的目录<TestName>.logs分别为每个测试。
注意标签s1
和s2
,这是在盖规范文件,并在调用中使用ct_cover:cross_cover_analyse/2
。其目的仅仅是将封面规范中指定的模块映射到分析函数调用中指定的日志目录。标签名称除此之外没有任何意义。
10.6记录
要查看代码覆盖率测试的结果,请单击顶级索引页面中标记为“COVER LOG”的按钮以进行测试运行。
在Erlang / OTP 17.1之前,如果您的测试运行由多个测试组成,则测试运行期间每个测试都会启动并停止覆盖。单独的日志将通过测试套件结果页面上的“覆盖日志”链接提供。这些链接仍然可用,但现在它们都指向与顶级索引页面上的按钮相同的页面。日志包含完整测试运行的累计结果。有关此更改的详细信息,请参阅发行说明。
该按钮会将您带到代码覆盖率概述页面。如果您已成功执行详细的覆盖率分析,则可在此处找到指向每个单独模块覆盖率页面的链接。
如果进行交叉覆盖分析,并且有当前测试的累计覆盖率结果,则“通过所有测试收集的覆盖率数据”链接会将您带到这些结果中。