查看源代码 代码覆盖率分析

通用

尽管 Common Test 最初是为黑盒测试而创建的,但它也完全可以作为白盒测试工具使用。当要测试的应用程序是用 Erlang 编写的时,尤其如此。在这种情况下,测试端口很容易通过 Erlang 函数调用来实现。

在对 Erlang 应用程序进行白盒测试时,能够测量测试的代码覆盖率非常有用。Common Test为此目的提供了对 OTP Cover 工具的简单访问。Common Test处理与 Cover 工具的所有必要通信(启动、编译、分析等等)。Common Test 用户只需要指定代码覆盖率分析的范围。

使用

要指定要包含在代码覆盖率测试中的模块,请提供一个覆盖率规范文件。通过此文件,您可以指出特定模块或指定包含要包含在分析中的模块的目录。您还可以指定要从分析中排除的模块。

如果您正在测试一个分布式 Erlang 应用程序,那么您希望包含在代码覆盖率分析中的代码很可能会在与 Common Test 运行的节点不同的 Erlang 节点上执行。如果是这样,您必须在覆盖率规范文件中指定这些其他节点,或者将它们动态添加到代码覆盖率节点集中。有关后者的详细信息,请参阅模块 ct_cover

在覆盖率规范文件中,您还可以指定所需的代码覆盖率分析级别:detailsoverview。在详细模式下,您会获得一个覆盖率概览页面,其中显示每个模块和总覆盖率百分比。您还会获得为分析中包含的每个模块打印的 HTML 文件,其中精确显示了测试期间执行的代码部分。在概览模式下,仅打印代码覆盖率概览页面。

您可以选择在测试之间导出和导入代码覆盖率数据。如果您在覆盖率规范文件中指定导出文件的名称,则 Common Test 会在测试结束时将收集到的覆盖率数据导出到此文件。类似地,您可以指定先前导出的数据以导入并包含在测试分析中(可以指定多个导入文件)。这样,就可以分析总的代码覆盖率,而无需一次运行所有测试。

要激活代码覆盖率支持,请在启动 Common Test 时指定覆盖率规范文件的名称。可以通过在 ct_run 中使用标志 -cover 来实现,例如

$ ct_run -dir $TESTOBJS/db -cover $TESTOBJS/db/config/db.coverspec

您还可以通过将 {cover,CoverSpec} 元组添加到参数 Opts 中,在调用 ct:run_test/1 时传递覆盖率规范文件名。

您还可以在测试规范中启用代码覆盖率(请参阅运行测试和分析结果部分中的 测试规范 部分)。

测试完成后停止 Cover 工具

默认情况下,测试完成后 Cover 工具会自动停止。这会导致原始(非覆盖编译)模块重新加载到测试节点中。如果此时进程仍在运行任何覆盖编译模块的旧代码,这意味着它在覆盖编译后没有执行任何完全限定的函数调用,则该进程将被终止。为了避免这种情况,请将选项 cover_stop 的值设置为 false。这意味着这些模块将保持覆盖编译状态。因此,仅当在测试完成后终止被测 Erlang 节点,或者可以手动停止覆盖时才建议这样做。

可以通过在 ct_run 中使用标志 -cover_stop 来设置此选项,也可以将 {cover_stop,true|false} 添加到 ct:run_test/1 的参数 Opts 中,或者在测试规范中添加 cover_stop 项(请参阅运行测试和分析结果部分中的 测试规范 部分)。

覆盖率规范文件

通用配置

以下是覆盖率规范文件中允许的一般配置项

%% 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_rexcl_dirs_r 告诉 Common Test 递归搜索指定的目录,并包含或排除在搜索过程中找到的任何模块。项 incl_dirsexcl_dirs 会对模块进行非递归搜索(也就是说,仅包含或排除在指定目录中找到的模块)。

注意

包含要包含在代码覆盖率测试中的 Erlang 模块的目录必须存在于代码服务器路径中。否则,Cover 工具无法重新编译这些模块。仅在 Common Test 的覆盖率规范文件中指定这些目录是不够的。

OTP 应用程序配置

当在测试 OTP 应用程序本身时使用覆盖率规范时,有一个特殊的 incl_app 指令,用于包含应用程序的模块以进行覆盖率编译。

{incl_app, AppName, Cover :: overview | details}.

注意

如果您希望将其他一些常规覆盖率配置与此选项一起使用,则应将 AppName 插入到选项及其值之间,从而创建一个三元组。

交叉覆盖率分析

交叉覆盖机制允许对多个测试中的模块进行覆盖率分析。如果某些代码(例如,库模块)被许多不同的测试使用,并且需要累积的覆盖率结果,则此机制非常有用。

通过在覆盖率规范中使用参数 export 并在离线状态下分析结果,也可以以更自定义的方式实现此目的。但是,交叉覆盖机制是一种内置解决方案,它还提供日志记录。

通过一个示例可以最容易地解释此机制

假设有两个系统,s1s2,它们在单独的测试运行中进行测试。系统 s1 包含一个库模块 m1,该模块由测试运行 s1 进行测试,并在 s1 的覆盖率规范中包含,如下所示

s1.cover:
  {incl_mods,[m1]}.

在分析代码覆盖率时,可以在 s1 测试结果的覆盖率日志中看到 m1 的结果。

现在,假设 m1 是一个库模块,它也经常被系统 s2 使用。测试运行 s2 没有专门测试 m1,但查看 s2 测试覆盖了 m1 的哪些部分仍然很有意义。为此,也可以在 s2 的覆盖率规范中包含 m1,如下所示

s2.cover:
  {incl_mods,[m1]}.

这也会在测试运行 s2 的覆盖率日志中为 m1 提供一个条目。问题是,这仅反映了 s2 测试的覆盖率,而不是 s1s2 的累积结果。这就是交叉覆盖机制的用武之地。

如果 s2 的覆盖率规范改为如下所示

s2.cover:
  {cross,[{s1,[m1]}]}.

那么 m1 将在测试运行 s2 中进行覆盖率编译,但不会显示在覆盖率日志中。相反,如果在 s1s2 测试运行都完成后调用 ct_cover:cross_cover_analyse/2,则 m1 的累积结果将在测试运行 s1 的交叉覆盖率日志中可用。

对分析函数的调用必须如下所示

ct_cover:cross_cover_analyse(Level, [{s1,S1LogDir},{s2,S2LogDir}]).

这里,S1LogDirS2LogDir 分别是每个测试的名为 <TestName>.logs 的目录。

请注意标签 s1s2,它们在覆盖率规范文件中以及在对 ct_cover:cross_cover_analyse/2 的调用中使用。它们的目的是仅将覆盖率规范中指定的模块映射到分析函数调用中指定的日志目录。标签名称除此之外没有任何含义。

日志记录

要查看代码覆盖率测试的结果,请单击测试运行的顶级索引页面中标记为“COVER LOG”的按钮。

在 Erlang/OTP 17.1 之前,如果您的测试运行包含多个测试,则会在测试运行中的每个测试启动和停止覆盖率。可以通过测试套件结果页面上的“覆盖率日志”链接访问单独的日志。这些链接仍然可用,但现在它们都指向与顶级索引页面上的按钮相同的页面。日志包含完整测试运行的累积结果。有关此更改的详细信息,请参阅发行说明。

该按钮将您带到代码覆盖率概览页面。如果您已成功执行详细的覆盖率分析,则可以在此处找到指向每个单独模块覆盖率页面的链接。

如果执行了交叉覆盖率分析,并且当前测试有累积的覆盖率结果,则链接“在所有测试中收集的覆盖率数据”将您带到这些结果。