查看源码 运行测试和分析结果
使用 Common Test 框架
Common Test 框架为测试提供了一个高级操作员接口,提供了以下功能
- 自动编译测试套件(和帮助模块)
- 创建额外的 HTML 页面以改进概述。
- 用于运行所有可用测试的单命令接口
- 处理指定与被测系统 (SUT) 相关的数据(以及任何其他可变数据)的配置文件
- 用于并行运行多个独立测试会话的模式,具有中央控制和配置
自动编译测试套件和帮助模块
当 Common Test 启动时,它会自动尝试编译指定测试中包含的任何套件。 如果指定了特定的套件,则只编译这些套件。 如果指定了特定的测试对象目录(意味着此目录中的所有套件都是测试的一部分),则 Common Test 会在该目录中运行函数 make:all/1 来编译这些套件。
如果一个或多个套件的编译失败,则编译错误会打印到 tty,并询问操作员是否要在缺少套件的情况下继续测试运行,还是中止测试运行。如果操作员选择继续,则 HTML 日志中会记录缺少套件的测试。 如果 Common Test 在编译失败后无法提示用户(如果 Common Test 不控制 stdin),则测试运行会自动继续,而不会包含缺少的套件。 但是,可以使用 ct_run 标志 -abort_if_missing_suites 或 ct:run_test/1 选项 {abort_if_missing_suites,TrueOrFalse} 来修改此行为。 如果 abort_if_missing_suites 设置为 true,则如果某些套件编译失败,测试运行会立即停止。
位于与测试套件相同的测试对象目录中的任何帮助模块(即,名称不以“_SUITE”结尾的常规 Erlang 模块)也是自动编译的。 帮助模块不会被误认为是测试套件(除非它具有“_SUITE”名称)。 特定测试对象目录中的所有帮助模块都会被编译,无论该目录中是全部还是仅特定套件是测试的一部分。
如果测试套件或帮助模块包含存储在测试目录以外的其他位置的头文件,则可以使用 ct_run 的标志 -include 或 ct:run_test/1 的选项 include 来指定这些包含目录。此外,可以使用 OS 环境变量 CT_INCLUDE_PATH 指定包含路径。
示例 (bash)
$ export CT_INCLUDE_PATH=~testuser/common_suite_files/include:~testuser/common_lib_files/include
Common Test 将所有包含目录(使用标志/选项 include 或变量 CT_INCLUDE_PATH 指定,或两者都指定)传递给编译器。
包含目录也可以在测试规范中指定,请参见 测试规范。
如果用户只想通过指定顶级目录(例如,使用启动标志/选项 dir)来运行测试对象(或 OTP 应用程序)的所有测试套件,则 Common Test 主要在名为 test 的子目录中查找测试套件模块。 如果此子目录不存在,则假定指定的顶级目录是测试目录,并从那里读取测试套件。
要禁用自动编译功能,请使用 ct_run 的标志 -no_auto_compile 或 ct:run_test/1 的选项 {auto_compile,false}。禁用自动编译后,用户负责在测试运行之前编译测试套件模块(和任何帮助模块)。 如果在 Common Test 启动期间无法从本地文件系统加载模块,则用户必须在开始测试之前预加载模块。Common Test 仅验证指定的测试套件是否存在(即,它们是否已加载或可以加载)。例如,如果测试套件是通过 RPC 从远程节点传输并作为二进制文件加载的,则这非常有用。
从 OS 命令行运行测试
可以使用 ct_run 程序从 OS 命令行运行测试,例如,如下所示
ct_run -config <configfilenames> -dir <dirs>ct_run -config <configfilenames> -suite <suiteswithfullpath>ct_run -userconfig <callbackmodulename> <configfilenames> -suite <suiteswithfullpath>ct_run -config <configfilenames> -suite <suitewithfullpath> -group <groups> -case <casenames>
示例
$ ct_run -config $CFGS/sys1.cfg $CFGS/sys2.cfg -dir $SYS1_TEST $SYS2_TEST
$ ct_run -userconfig ct_config_xml $CFGS/sys1.xml $CFGS/sys2.xml -dir $SYS1_TEST $SYS2_TEST
$ ct_run -suite $SYS1_TEST/setup_SUITE $SYS2_TEST/config_SUITE
$ ct_run -suite $SYS1_TEST/setup_SUITE -case start stop
$ ct_run -suite $SYS1_TEST/setup_SUITE -group installation -case start stop可以组合标志 dir、suite 和 group/case。例如,要在目录 testdir 中运行 x_SUITE 和 y_SUITE,如下所示
$ ct_run -dir ./testdir -suite x_SUITE y_SUITE这与以下内容具有相同的效果
$ ct_run -suite ./testdir/x_SUITE ./testdir/y_SUITE有关详细信息,请参见 测试用例组执行。
以下标志也可以与 ct_run 一起使用
-help- 列出所有可用的启动标志。-logdir <dir>- 指定 HTML 日志文件的写入位置。-label <name_of_test_run>- 将测试运行与一个名称关联起来,该名称会打印在概述 HTML 日志文件中。-refresh_logs- 刷新顶层 HTML 索引文件。-shell- 启动交互式 shell 模式(稍后描述)。-step [step_opts]- 使用 Erlang 调试器单步执行测试用例(稍后描述)。-spec <testspecs>- 使用测试规范作为输入(稍后描述)。-allow_user_terms- 允许测试规范中使用用户特定的术语(稍后描述)。-silent_connections [conn_types]- 告诉Common Test抑制指定连接的打印输出(稍后描述)。-stylesheet <css_file>- 指向用户 HTML 样式表(稍后描述)。-cover <cover_cfg_file>- 执行代码覆盖率测试(请参见 代码覆盖率分析)。-cover_stop <bool>- 指定是否在测试完成后停止cover工具(请参见 代码覆盖率分析)。-event_handler <event_handlers>- 安装事件处理程序。-event_handler_init <event_handlers>- 安装事件处理程序,包括启动参数。-ct_hooks <ct_hooks>- 安装 Common Test 钩子,包括启动参数。-ct_hooks_order [test|config]- 修改 Common Test 钩子的执行顺序。-enable_builtin_hooks <bool>- 启用或禁用 内置 Common Test 钩子。默认值为true。-include- 指定包含目录(前面已描述)。-no_auto_compile- 禁用自动测试套件编译功能(前面已描述)。-abort_if_missing_suites- 如果一个或多个套件编译失败,则中止测试运行(前面已描述)。-multiply_timetraps <n>- 扩展 时间陷阱超时值。-scale_timetraps <bool>- 启用自动 时间陷阱超时缩放。-repeat <n>- 告诉Common Test将测试重复n次(稍后描述)。-duration <time>- 告诉Common Test将测试重复运行一段时间(稍后描述)。-until <stop_time>- 告诉Common Test将测试重复运行到stop_time(稍后描述)。-force_stop [skip_rest]- 超时时,当当前测试作业完成时,测试运行将中止。 如果提供了skip_rest,则会跳过当前测试作业中剩余的测试用例(稍后描述)。-decrypt_key <key>- 为加密配置文件提供解密密钥。-decrypt_file <key_file>- 指出包含加密配置文件的解密密钥的文件。-basic_html- 关闭可能与旧浏览器不兼容的 HTML 增强功能。-logopts <opts>- 启用对日志记录行为的修改,请参见 日志选项。-verbosity <levels>- 设置打印输出的详细级别。-no_esc_chars- 禁用特殊 HTML 字符的自动转义。 请参见日志记录章节。
注意
传递给
Common Test的目录可以具有相对路径或绝对路径。
注意
Erlang 运行时系统(应用程序 ERTS)的任何启动标志也可以作为参数传递给
ct_run。 例如,能够使用标志-pa或-pz将要添加到 Erlang 代码服务器搜索路径的目录传递进来非常有用。 如果你有测试套件的通用帮助模块或库模块(单独编译),存储在测试套件目录之外的其他目录中,最好以这种方式将这些help/lib目录添加到代码路径中。示例
$ ct_run -dir ./chat_server -logdir ./chat_server/testlogs -pa $PWD/chat_server/ebin目录
chat_server/ebin的绝对路径在此处传递给代码服务器。这至关重要,因为相对路径由代码服务器存储为相对路径,而Common Test在测试运行期间会更改 ERTS 的当前工作目录。
ct_run 程序在关闭前设置退出状态。定义了以下值:
0表示测试运行成功,即没有失败或自动跳过的测试用例。1表示一个或多个测试用例失败或被自动跳过。2表示测试执行失败,例如,由于编译错误或信息函数返回非法值。
如果自动跳过的测试用例不影响退出状态。可以使用启动标志更改默认行为。
-exit_status ignore_config注意
执行不带启动标志的
ct_run等同于命令:ct_run -dir ./
有关 ct_run 程序的更多信息,请参阅模块 ct_run 和 安装 部分。
从 Erlang Shell 或 Erlang 程序运行测试
Common Test 提供了一个用于运行测试的 Erlang API。用于指定和执行测试的主要(也是最灵活的)函数是 ct:run_test/1。它接受与 ct_run 相同的启动参数,但这些标志改为在键值元组列表中指定为选项。例如,使用 ct_run 指定的测试如下所示:
$ ct_run -suite ./my_SUITE -logdir ./results
使用 ct:run_test/1 指定为:
1> ct:run_test([{suite,"./my_SUITE"},{logdir,"./results"}]).
该函数返回测试结果,由元组 {Ok,Failed,{UserSkipped,AutoSkipped}} 表示,其中每个元素都是一个整数。如果测试执行失败,该函数返回元组 {error,Reason},其中术语 Reason 解释了失败的原因。
如果使用空选项列表调用该函数,则会使用默认的启动选项 {dir,Cwd}(在当前工作目录中运行所有套件)。
释放 Erlang Shell
在使用 ct:run_test/1 启动的测试执行期间,控制 stdin 的 Erlang shell 进程仍然是 Common Test 进程系统的顶层进程。因此,在测试运行期间,Erlang shell 不可用于交互。如果这不是期望的结果,例如,因为 shell 需要用于调试目的或在测试执行期间与 SUT 交互,请将启动选项 release_shell 设置为 true(在调用 ct:run_test/1 时,或使用稍后描述的相应测试规范项)。这使得 Common Test 在测试套件编译阶段后立即释放 shell。为了实现这一点,会生成一个测试运行程序进程来控制测试执行。其结果是 ct:run_test/1 返回此进程的 pid,而不是测试结果,而测试结果将在测试运行结束时打印到 tty。
注意
要使用函数
ct:break/1,2和ct:continue/0,1,必须 将release_shell设置为true。
有关详细信息,请参阅 ct:run_test/1 手册页。
测试用例组执行
使用 ct_run 标志或 ct:run_test/1 选项 group,可以指定一个或多个测试用例组,还可以选择与特定测试用例组合使用。在命令行上指定组的语法如下:
$ ct_run -group <group_names_or_paths> [-case <cases>]在 Erlang shell 中的语法如下:
1> ct:run_test([{group,GroupsNamesOrPaths}, {case,Cases}]).参数 group_names_or_paths 指定一个或多个组名称和/或一个或多个组路径。在启动时,Common Test 在组定义树中搜索匹配的组(即从 Suite:groups/0 返回的列表;有关详细信息,请参阅 测试用例组 部分)。
给定一个组名称,例如 g,Common Test 会搜索所有通向 g 的路径。路径是指一系列嵌套的组,必须按顺序到达才能从顶层组到达 g。要执行组 g 中的测试用例,Common Test 必须为通向 g 的路径中的每个组调用 init_per_group/2 函数,然后调用所有相应的 end_per_group/2 函数。这是因为 g 中的测试用例的配置(及其 Config 输入数据)取决于 init_per_testcase(TestCase, Config) 及其返回值,而这又取决于 init_per_group(g, Config) 及其返回值,而这又取决于 g 上方组的 init_per_group/2,依此类推,一直到顶层组。
这意味着,如果存在多种在路径中定位组(及其测试用例)的方法,则组搜索操作的结果是一些测试,所有测试都将执行。Common Test 将由单个名称组成的组规范解释如下:
“搜索并找到组定义树中通向指定组的所有路径,并且对于每个路径,创建一个按顺序执行以下操作的测试:
- 执行通向指定组的路径中的所有配置函数。
- 执行此组中的所有或所有匹配的测试用例。
- 执行该组的所有子组中的所有或所有匹配的测试用例。”
用户可以使用参数 group_names_or_paths 指定特定的组路径。通过这种类型的规范,可以避免执行不需要的组(在其他匹配的路径中)和/或执行子组。组路径的命令行语法是路径中组名称的列表,例如:
$ ct_run -suite "./x_SUITE" -group [g1,g3,g4] -case tc1 tc5
Erlang shell 中的语法如下(需要在组列表内使用列表):
1> ct:run_test([{suite,"./x_SUITE"}, {group,[[g1,g3,g4]]}, {testcase,[tc1,tc5]}]).
指定路径中的最后一个组是测试中的终止组,也就是说,不执行此组后面的子组。在前面的示例中,g4 是终止组。因此,Common Test 执行一个测试,该测试调用通向 g4 的路径中的所有 init 配置函数,即 g1..g3..g4。然后,它在 g4 中调用测试用例 tc1 和 tc5,最后按顺序调用所有 end 配置函数 g4..g3..g1。
注意
组路径规范不必包含通向终止组的路径中的所有组。如果指定了不完整的组路径,
Common Test会搜索所有匹配的路径。
注意
组名称和组路径可以与参数
group_names_or_paths组合使用。每个元素都被视为与参数cases结合使用的单独规范。以下示例对此进行了说明。
示例
-module(x_SUITE).
...
%% The group definitions:
groups() ->
[{top1,[],[tc11,tc12,
{sub11,[],[tc12,tc13]},
{sub12,[],[tc14,tc15,
{sub121,[],[tc12,tc16]}]}]},
{top2,[],[{group,sub21},{group,sub22}]},
{sub21,[],[tc21,{group,sub2X2}]},
{sub22,[],[{group,sub221},tc21,tc22,{group,sub2X2}]},
{sub221,[],[tc21,tc23]},
{sub2X2,[],[tc21,tc24]}].以下代码执行两个测试,一个用于 top1 下的所有用例和所有子组,另一个用于 top2 下的所有用例和子组:
$ ct_run -suite "x_SUITE" -group all1> ct:run_test([{suite,"x_SUITE"}, {group,all}]).使用 -group top1 top2 或 {group,[top1,top2]} 会得到相同的结果。
以下代码执行一个测试,用于 top1 下的所有用例和子组:
$ ct_run -suite "x_SUITE" -group top11> ct:run_test([{suite,"x_SUITE"}, {group,[top1]}]).以下代码运行一个测试,该测试执行 top1 和 top1 下任何子组(可以在其中找到 sub11 和 sub121)中的 tc12:
$ ct_run -suite "x_SUITE" -group top1 -case tc121> ct:run_test([{suite,"x_SUITE"}, {group,[top1]}, {testcase,[tc12]}]).以下代码仅在组 top1 中执行 tc12:
$ ct_run -suite "x_SUITE" -group [top1] -case tc121> ct:run_test([{suite,"x_SUITE"}, {group,[[top1]]}, {testcase,[tc12]}]).以下代码在 top1 及其所有子组中搜索 tc16,导致此测试用例在组 sub121 中执行:
$ ct_run -suite "x_SUITE" -group top1 -case tc161> ct:run_test([{suite,"x_SUITE"}, {group,[top1]}, {testcase,[tc16]}]).在此示例中使用特定路径 -group [sub121] 或 {group,[[sub121]]} 会得到相同的结果。
以下代码执行两个测试,一个包括 sub12 下的所有用例和子组,另一个仅包含 sub12 中的测试用例:
$ ct_run -suite "x_SUITE" -group sub12 [sub12]1> ct:run_test([{suite,"x_SUITE"}, {group,[sub12,[sub12]]}]).在以下示例中,Common Test 查找并执行两个测试,一个用于从 top2 到 sub2X2 经过 sub21 的路径,另一个用于从 top2 到 sub2X2 经过 sub22 的路径:
$ ct_run -suite "x_SUITE" -group sub2X21> ct:run_test([{suite,"x_SUITE"}, {group,[sub2X2]}]).在以下示例中,通过指定唯一路径 top2 -> sub21 -> sub2X2,仅执行一个测试。从 top2 到 sub2X2 的第二个可能路径(来自前面的示例)将被丢弃:
$ ct_run -suite "x_SUITE" -group [sub21,sub2X2]1> ct:run_test([{suite,"x_SUITE"}, {group,[[sub21,sub2X2]]}]).以下代码仅执行 sub22 的测试用例,并且与组定义相比,顺序相反:
$ ct_run -suite "x_SUITE" -group [sub22] -case tc22 tc211> ct:run_test([{suite,"x_SUITE"}, {group,[[sub22]]}, {testcase,[tc22,tc21]}]).如果属于组的测试用例(根据组定义)在没有组规范的情况下执行,也就是说,只是通过(使用命令行):
$ ct_run -suite "my_SUITE" -case my_tc
或(使用 Erlang shell):
1> ct:run_test([{suite,"my_SUITE"}, {testcase,my_tc}]).
然后,Common Test 会忽略组定义,并仅在测试套件的范围内执行测试用例(不调用任何组配置函数)。
本节中介绍的组规范功能也可以在 测试规范 中使用(添加了一些额外的功能)。
运行交互式 Shell 模式
您可以在交互式 shell 模式下启动 Common Test,在该模式下不执行自动测试。相反,Common Test 启动其实用程序进程,安装配置数据(如果有),并等待用户从 Erlang shell 调用函数(通常是测试用例支持函数)。
shell 模式非常有用,例如,用于调试测试套件,在“模拟”测试用例执行期间分析和调试 SUT,以及在测试套件开发期间尝试各种操作。
要启动交互式 shell 模式,请手动启动 Erlang shell 并调用 ct:install/1 以安装您可能需要的任何配置数据(否则使用 [] 作为参数)。然后调用 ct:start_interactive/0 以启动 Common Test。
如果您使用 ct_run 程序,则可以使用标志 -shell 以及可选的标志 -config 和/或 -userconfig 一次性启动 Erlang shell 和 Common Test。
示例
ct_run -shellct_run -shell -config cfg/db.cfgct_run -shell -userconfig db_login testuser x523qZ
如果使用命令 ct_run 时未指定配置文件,则会显示警告。如果之前已从同一目录运行过 Common Test,则会再次使用相同的配置文件。如果之前未从此目录运行过 Common Test,则没有可用的配置文件。
如果要从 Erlang shell 调用任何使用“必需配置数据”的函数(例如,函数 ct_telnet 或 ct_ftp),请首先使用 ct:require/1,2 请求配置数据。这等同于在 测试套件信息函数 或 测试用例信息函数 中的 require 语句。
示例
1> ct:require(unix_telnet, unix).
ok
2> ct_telnet:open(unix_telnet).
{ok,<0.105.0>}
4> ct_telnet:cmd(unix_telnet, "ls .").
{ok,["ls .","file1 ...",...]}Common Test 通常在测试用例日志中打印的所有内容,在交互模式下都会写入到名为 ctlog.html 的日志中,该日志位于目录 ct_run.<timestamp> 中。可以通过执行 ct_run 的目录中的名为 last_interactive.html 的文件中找到指向此文件的链接。不支持为日志指定与当前工作目录不同的根目录。
如果希望退出交互模式(例如,使用 ct:run_test/1 启动自动化测试运行),请调用函数 ct:stop_interactive/0。这将关闭正在运行的 ct 应用程序。使用 require 创建的配置名称与数据之间的关联也会被删除。函数 ct:start_interactive/0 会将您带回交互模式,但不会恢复之前的状态。
使用 Erlang 调试器逐步执行测试用例
使用 ct_run -step [opts],或者将选项 {step,Opts} 传递给 ct:run_test/1,可以实现以下操作
- 自动启动 Erlang 调试器。
- 使用其图形界面来调查当前测试用例的状态。
- 逐步执行测试用例和/或设置执行断点。
如果使用标志/选项 step 未指定任何额外选项,则会自动在 Common Test 将要执行的测试用例以及仅在这些函数上设置断点。如果指定了 step 选项 config,则也会在套件中的配置函数上初始设置断点,即 init_per_suite/1、end_per_suite/1、init_per_group/2、end_per_group/2、init_per_testcase/2 和 end_per_testcase/2。
Common Test 启用了调试器自动附加功能,这意味着对于每个开始执行的新解释测试用例函数,都会自动弹出一个新的跟踪窗口(因为每个测试用例都在专用的 Erlang 进程上执行)。每当开始新的测试用例时,Common Test 都会尝试关闭上一个测试用例的非活动跟踪窗口。但是,如果您希望 Common Test 保留非活动跟踪窗口,请使用选项 keep_inactive。
step 功能可以与标志/选项 suite 和 suite + case/testcase 一起使用,但不能与 dir 一起使用。
测试规范
通用描述
指定要测试内容的 最灵活方式是使用测试规范,测试规范是一系列 Erlang 项。这些项通常在一个或多个文本文件中声明(请参阅 ct:run_test/1),也可以以列表的形式传递给 Common Test(请参阅 ct:run_testspec/1)。有两种通用类型的项:配置项和测试规范项。
使用配置项,可以执行以下操作,例如
- 标记测试运行(类似于
ct_run -label)。 - 在开始测试之前评估任何表达式。
- 导入配置数据(类似于
ct_run -config/-userconfig)。 - 指定顶层 HTML 日志目录(类似于
ct_run -logdir)。 - 启用代码覆盖率分析(类似于
ct_run -cover)。 - 安装
Common Test Hooks(类似于ct_run -ch_hooks)。 - 安装
event_handler插件(类似于ct_run -event_handler)。 - 指定要传递给编译器的包含目录以进行自动编译(类似于
ct_run -include)。 - 禁用自动编译功能(类似于
ct_run -no_auto_compile)。 - 设置详细级别(类似于
ct_run -verbosity)。
配置项可以与 ct_run 启动标志或 ct:run_test/1 选项结合使用。结果是,对于某些标志/选项和项,值会合并(例如,配置文件、包含目录、详细级别和静默连接),而对于其他标志/选项和项,启动标志/选项会覆盖测试规范项(例如,日志目录、标签、样式表和自动编译)。
使用测试规范项,可以准确说明要运行哪些测试以及按什么顺序运行。测试项指定一个或多个套件、一个或多个测试用例组(可能嵌套),或者一个组(或多个组)或套件中的一个或多个测试用例。
可以按顺序声明任意数量的测试项。默认情况下,Common Test 将这些项编译为一个或多个要在一个最终测试运行中执行的测试。指定一组测试用例的项会“吞噬”仅指定这些用例子集的项。例如,合并一个指定要执行套件 S 中的所有用例的项,与另一个仅指定 S 中测试用例 X 和 Y 的项的结果是对 S 中所有用例的测试。但是,如果指定 S 中测试用例 X 和 Y 的项与指定 S 中用例 Z 的项合并,则结果是对 S 中 X、Y 和 Z 的测试。要禁用此行为,即改为以“脚本式”方式按顺序执行每个测试,请在测试规范中将项 merge_tests 设置为 false。
测试项还可以指定一个或多个要跳过的测试套件、组或测试用例。跳过的套件、组和用例不会执行,并在 HTML 日志文件中显示为 SKIPPED。
使用多个测试规范文件
当在启动时指定多个测试规范文件时(使用 ct_run -spec file1 file2 ... 或 ct:run_test([{spec, [File1,File2,...]}])),Common Test 会为每个规范文件执行一个测试运行,或合并这些文件并在单个测试运行中执行所有测试。第一个行为是默认行为。后者要求提供启动标志/选项 join_specs,例如,run_test -spec ./my_tests1.ts ./my_tests2.ts -join_specs。
合并多个规范或单独运行它们也可以通过(并且可以与)测试规范文件包含来实现。
测试规范文件包含
使用项 specs,测试规范可以包含其他规范。包含的规范可以与源规范合并,也可以用于生成单独的测试运行(如上面的启动标志/选项 join_specs)。
示例
%% In specification file "a.spec"
{specs, join, ["b.spec", "c.spec"]}.
{specs, separate, ["d.spec", "e.spec"]}.
%% Config and test terms follow
...在此示例中,在文件 “b.spec” 和 “c.spec” 中定义的测试项与源规范 “a.spec” 中的项合并(如果有)。包含规范 “d.spec” 和 “e.spec” 会产生两个单独且独立的测试运行(每个包含的规范一个)。
选项 join 并不意味着测试项会合并,仅意味着所有测试都会在一个测试运行中执行。
合并的规范共享通用配置设置,例如 config 文件或 include 目录的列表。对于无法合并的配置,例如 logdir 或 verbosity 的设置,用户有责任确保在合并测试规范时没有冲突。使用选项 separate 包含的规范不与源规范共享配置设置。例如,如果包含的规范中有冲突的配置设置,使其无法合并,这会很有用。
如果在源规范中设置了 {merge_tests,true}(这是默认设置),则合并规范中的项会与源规范中的项合并(根据前面 merge_tests 的描述)。
请注意,与其他规范合并时,始终使用源规范中的 merge_tests 设置。例如,假设源规范 A,其中包含测试 TA1 和 TA2,已设置 {merge_tests,false},并且它包含另一个规范 B,其中包含测试 TB1 和 TB2,已设置 {merge_tests,true}。结果是执行测试序列 TA1,TA2,merge(TB1,TB2)。相反的 merge_tests 设置将导致测试序列 merge(merge(TA1,TA2),TB1,TB2)。
项 specs 可用于嵌套规范,即让一个规范包含其他规范,而后者又包含其他规范,依此类推。
测试用例组
当指定一个测试用例组时,将执行以下操作:首先执行函数 init_per_group,然后执行所有测试用例和子组(包括它们的配置函数),最后执行函数 end_per_group。此外,如果指定了组中的特定测试用例,则会调用该组的 init_per_group 和 end_per_group。如果定义为另一个组的子组的组(在 Suite:groups/0 中定义)被指定(或者如果指定了子组的特定测试用例),Common Test 会调用顶级组和相关子组的配置函数(从而可以将配置数据从 init_per_suite 一直传递到子组中的测试用例)。
测试规范使用相同的机制,通过名称和路径指定测试用例组,如 测试用例组执行 部分所述,并添加了元素 GroupSpec。
元素 GroupSpec 可以指定组执行属性,这些属性会覆盖组定义中的属性(即在 groups/0 中)。子组的执行属性也可以被覆盖。此功能可以在执行时更改组的属性,而无需编辑测试套件。 Suite:all/0 列表中的 group 元素也具有相同的功能。有关详细信息和示例,请参阅 测试用例组 部分。
测试规范语法
测试规范可以用于在单个测试主机环境和分布式 Common Test 环境(大规模测试)中运行测试。术语 init 中的节点参数仅在后者中相关(请参阅大规模测试中的 测试规范 部分)。有关各个术语的详细信息,请参阅用户指南中的相应部分,例如,以下内容:
ct_run程序 可用于查看可用启动标志的概述(因为大多数标志都有相应的配置项)。- 日志记录(适用于术语
verbosity、stylesheet、basic_html和esc_chars)。 - 外部配置数据(适用于术语
config和userconfig)。 - 事件处理(适用于
event_handler术语)。 - Common Test 钩子(适用于术语
ct_hooks)。
配置术语
{merge_tests, Bool}.
{define, Constant, Value}.
{specs, InclSpecsOption, TestSpecs}.
{node, NodeAlias, Node}.
{init, InitOptions}.
{init, [NodeAlias], InitOptions}.
{label, Label}.
{label, NodeRefs, Label}.
{verbosity, VerbosityLevels}.
{verbosity, NodeRefs, VerbosityLevels}.
{stylesheet, CSSFile}.
{stylesheet, NodeRefs, CSSFile}.
{silent_connections, ConnTypes}.
{silent_connections, NodeRefs, ConnTypes}.
{multiply_timetraps, N}.
{multiply_timetraps, NodeRefs, N}.
{scale_timetraps, Bool}.
{scale_timetraps, NodeRefs, Bool}.
{cover, CoverSpecFile}.
{cover, NodeRefs, CoverSpecFile}.
{cover_stop, Bool}.
{cover_stop, NodeRefs, Bool}.
{include, IncludeDirs}.
{include, NodeRefs, IncludeDirs}.
{auto_compile, Bool},
{auto_compile, NodeRefs, Bool},
{abort_if_missing_suites, Bool},
{abort_if_missing_suites, NodeRefs, Bool},
{config, ConfigFiles}.
{config, ConfigDir, ConfigBaseNames}.
{config, NodeRefs, ConfigFiles}.
{config, NodeRefs, ConfigDir, ConfigBaseNames}.
{userconfig, {CallbackModule, ConfigStrings}}.
{userconfig, NodeRefs, {CallbackModule, ConfigStrings}}.
{logdir, LogDir}.
{logdir, NodeRefs, LogDir}.
{logopts, LogOpts}.
{logopts, NodeRefs, LogOpts}.
{create_priv_dir, PrivDirOption}.
{create_priv_dir, NodeRefs, PrivDirOption}.
{event_handler, EventHandlers}.
{event_handler, NodeRefs, EventHandlers}.
{event_handler, EventHandlers, InitArgs}.
{event_handler, NodeRefs, EventHandlers, InitArgs}.
{ct_hooks, CTHModules}.
{ct_hooks, NodeRefs, CTHModules}.
{ct_hooks_order, CTHOrder}.
{enable_builtin_hooks, Bool}.
{basic_html, Bool}.
{basic_html, NodeRefs, Bool}.
{esc_chars, Bool}.
{esc_chars, NodeRefs, Bool}.
{release_shell, Bool}.测试术语
{suites, Dir, Suites}.
{suites, NodeRefs, Dir, Suites}.
{groups, Dir, Suite, Groups}.
{groups, NodeRefs, Dir, Suite, Groups}.
{groups, Dir, Suite, Groups, {cases,Cases}}.
{groups, NodeRefs, Dir, Suite, Groups, {cases,Cases}}.
{cases, Dir, Suite, Cases}.
{cases, NodeRefs, Dir, Suite, Cases}.
{skip_suites, Dir, Suites, Comment}.
{skip_suites, NodeRefs, Dir, Suites, Comment}.
{skip_groups, Dir, Suite, GroupNames, Comment}.
{skip_groups, NodeRefs, Dir, Suite, GroupNames, Comment}.
{skip_cases, Dir, Suite, Cases, Comment}.
{skip_cases, NodeRefs, Dir, Suite, Cases, Comment}.Bool = true | false
Constant = atom()
Value = term()
InclSpecsOption = join | separate
TestSpecs = string() | [string()]
NodeAlias = atom()
Node = node()
NodeRef = NodeAlias | Node | master
NodeRefs = all_nodes | [NodeRef] | NodeRef
InitOptions = term()
Label = atom() | string()
VerbosityLevels = integer() | [{Category,integer()}]
Category = atom()
CSSFile = string()
ConnTypes = all | [atom()]
N = integer()
CoverSpecFile = string()
IncludeDirs = string() | [string()]
ConfigFiles = string() | [string()]
ConfigDir = string()
ConfigBaseNames = string() | [string()]
CallbackModule = atom()
ConfigStrings = string() | [string()]
LogDir = string()
LogOpts = [term()]
PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
EventHandlers = atom() | [atom()]
InitArgs = [term()]
CTHModules = [CTHModule |
{CTHModule, CTHInitArgs} |
{CTHModule, CTHInitArgs, CTHPriority}]
CTHModule = atom()
CTHInitArgs = term()
CTHOrder = test | config
Dir = string()
Suites = atom() | [atom()] | all
Suite = atom()
Groups = GroupPath | GroupSpec | [GroupSpec] | all
GroupPath = [[GroupSpec]]
GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,[GroupSpec]}
GroupName = atom()
GroupNames = GroupName | [GroupName]
Cases = atom() | [atom()] | all
Comment = string() | ""上述 config 术语之间的区别在于,使用 ConfigDir 时,ConfigBaseNames 是基本名称的列表,即不包含目录路径。ConfigFiles 必须是完整名称,包括路径。例如,以下两个术语具有相同的含义:
{config, ["/home/testuser/tests/config/nodeA.cfg",
"/home/testuser/tests/config/nodeB.cfg"]}.
{config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.注意
如果在
ct_run -spec TestSpecFile ...或ct:run:test([{spec,TestSpecFile},...])执行测试时,测试规范中指定的任何相对路径都相对于包含测试规范文件的目录。如果
ct:run:testspec(TestSpec)执行测试,则路径相对于顶级日志目录。
常量
术语 define 引入一个常量,用于将名称 Constant 替换为 Value,无论在测试规范中何处找到它。此替换发生在初始遍历测试规范期间。常量可以用于测试规范中的任何位置,例如,在任何列表和元组中,甚至在字符串和其它常量定义的值部分中。常量也可以是节点名称的一部分,但这是常量可以是原子的一部分的唯一位置。
注意
为了便于阅读,常量名称必须始终以大写字母、
$、?或_开头。这意味着它必须始终用单引号引起来(因为常量名称是原子,而不是文本)。
常量的主要好处是它们可以用于减小长字符串(如文件路径)的大小(并避免重复)。
示例
%% 1a. no constant
{config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.
{suites, "/home/testuser/tests/suites", all}.
%% 1b. with constant
{define, 'TESTDIR', "/home/testuser/tests"}.
{config, "'TESTDIR'/config", ["nodeA.cfg","nodeB.cfg"]}.
{suites, "'TESTDIR'/suites", all}.
%% 2a. no constants
{config, [testnode@host1, testnode@host2], "../config", ["nodeA.cfg","nodeB.cfg"]}.
{suites, [testnode@host1, testnode@host2], "../suites", [x_SUITE, y_SUITE]}.
%% 2b. with constants
{define, 'NODE', testnode}.
{define, 'NODES', ['NODE'@host1, 'NODE'@host2]}.
{config, 'NODES', "../config", ["nodeA.cfg","nodeB.cfg"]}.
{suites, 'NODES', "../suites", [x_SUITE, y_SUITE]}.常量使先前版本的 Common Test 中的测试规范术语 alias 变得冗余。此术语已弃用,但在即将发布的 Common Test 版本中仍受支持。不过,强烈建议将 alias 术语替换为 define。以下是此类替换的示例:
%% using the old alias term
{config, "/home/testuser/tests/config/nodeA.cfg"}.
{alias, suite_dir, "/home/testuser/tests/suites"}.
{groups, suite_dir, x_SUITE, group1}.
%% replacing with constants
{define, 'TestDir', "/home/testuser/tests"}.
{define, 'CfgDir', "'TestDir'/config"}.
{define, 'SuiteDir', "'TestDir'/suites"}.
{config, 'CfgDir', "nodeA.cfg"}.
{groups, 'SuiteDir', x_SUITE, group1}.常量也可以很好地替换术语 node,但这仍然具有声明性价值,主要是在与 NodeRefs == all_nodes 结合使用时(请参阅 类型)。
示例
下面是一个简单的测试规范示例:
{define, 'Top', "/home/test"}.
{define, 'T1', "'Top'/t1"}.
{define, 'T2', "'Top'/t2"}.
{define, 'T3', "'Top'/t3"}.
{define, 'CfgFile', "config.cfg"}.
{logdir, "'Top'/logs"}.
{config, ["'T1'/'CfgFile'", "'T2'/'CfgFile'", "'T3'/'CfgFile'"]}.
{suites, 'T1', all}.
{skip_suites, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}.
{skip_cases, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}.
{skip_cases, 'T1', t1C_SUITE, [test1], "Ignore"}.
{suites, 'T2', [t2B_SUITE,t2C_SUITE]}.
{cases, 'T2', t2A_SUITE, [test4,test1,test7]}.
{skip_suites, 'T3', all, "Not implemented"}.该示例指定以下内容:
- 指定的
logdir目录用于存储 HTML 日志文件(在以节点名称、日期和时间标记的子目录中)。 - 导入指定的测试系统配置文件中的变量以进行测试。
- 要运行的第一个测试包括系统
t1的所有套件。套件t1B和t1D从测试中排除。t1A中的测试用例test3和test4以及t1C中的test1用例也从测试中排除。 - 要运行的第二个测试用于系统
t2。包含的套件是t2B和t2C。t2A中的测试用例test4、test1和test7也被包含。测试用例按指定的顺序执行。 - 要运行的最后一个测试用于系统
t3。在这里,所有套件都被跳过,并在日志文件中明确指出。
init 术语
使用术语 init,可以为测试规范中定义的节点指定初始化选项。有一些选项可以启动节点并在节点上评估任何函数。有关详细信息,请参阅使用 Common Test 进行大规模测试部分中的 自动启动测试目标节点 部分。
用户特定术语
用户可以提供包含(对于 Common Test)无法识别的术语的测试规范。如果需要,在使用 ct_run 启动测试时使用标志 -allow_user_terms。这将强制 Common Test 忽略无法识别的术语。在此模式下,Common Test 无法像扫描器在默认模式下运行时那样有效地检查规范中的错误。如果使用 ct:run_test/1 启动测试,则可以通过元组 {allow_user_terms,true} 启用宽松的扫描器模式。
读取测试规范术语
可以查找当前测试规范中的术语(即用于配置和运行当前测试的规范)。函数 get_testspec_terms() 返回所有测试规范术语(包括配置术语和测试术语)的列表,而 get_testspec_terms(Tags) 返回与 Tags 中的标记(或标记)匹配的术语(或术语列表)。
例如,在测试规范中:
...
{label, my_server_smoke_test}.
{config, "../../my_server_setup.cfg"}.
{config, "../../my_server_interface.cfg"}.
...并且在例如,测试套件或 Common Test Hook 函数中:
...
[{label,[{_Node,TestType}]}, {config,CfgFiles}] =
ct:get_testspec_terms([label,config]),
[verify_my_server_cfg(TestType, CfgFile) || {Node,CfgFile} <- CfgFiles,
Node == node()];
...日志文件
随着测试套件的执行,事件以以下四种不同的方式记录:
- 文本到操作员控制台。
- 套件相关信息发送到主日志文件。
- 用例相关信息发送到次要日志文件。
- 使用测试结果更新 HTML 概述日志文件。
- 在名为
all_runs.html的日志中写入从某个目录执行的所有运行的链接,并将所有测试(最新结果)的直接链接写入顶级index.html。
通常,操作员(可能运行数百或数千个测试用例)不希望控制台充满有关特定测试用例的详细信息或输出。默认情况下,操作员仅看到以下内容:
- 确认测试已启动以及有关总共执行了多少个测试用例的信息。
- 有关每个失败的测试用例的小提示。
- 所有运行的测试用例的摘要。
- 测试运行完成时的确认。
- 一些特殊信息,例如错误报告、进度报告和使用
erlang:display/1或io:format/3编写的专门发送给除standard_io之外的接收器的打印输出(例如,默认的组领导进程user)。
要深入了解总体结果或特定测试用例的结果,操作员可以通过遵循 HTML 演示文稿中的链接并读取主要或次要日志文件来实现。 “all_runs.html”页面是一个很好的起点。它位于 logdir 中,并包含每个测试运行的链接,包括快速概述(包括日期和时间、节点名称、测试数量、测试名称和测试结果总计)。
为每个测试运行(即存储在以节点名称、日期和时间标记的 ct_run 目录中)写入一个“index.html”页面。此文件提供了在同一测试运行中执行的所有单个测试的概述。测试名称遵循以下约定:
TopLevelDir.TestDir(执行TestDir中的所有套件)TopLevelDir.TestDir:suites(执行特定的套件)TopLevelDir.TestDir.Suite(执行Suite中的所有用例)TopLevelDir.TestDir.Suite:cases(执行特定的测试用例)TopLevelDir.TestDir.Suite.Case(仅执行Case)
“测试运行索引”页面包含指向 Common Test 框架日志文件的链接,其中写入了有关导入的配置数据和一般测试进度的信息。此日志文件对于获取执行期间有关测试运行的快照信息很有用。它在分析测试结果或调试测试套件时也很有帮助。
“测试运行索引”页面指示测试是否有缺失的套件(即 Common Test 编译失败的套件)。可以在 Common Test 框架日志文件中找到缺失套件的名称。
主要日志文件显示测试运行的详细报告。它包括测试套件和测试用例名称、执行时间、失败的确切原因等。该信息以文本和 HTML 表示形式的文件提供。 HTML 文件显示一个摘要,该摘要提供了测试运行的良好概述。它还具有指向每个单独的测试用例日志文件的链接,以便使用 HTML 浏览器快速查看。
次要日志文件包含每个单独测试用例的完整详细信息,每个用例都位于单独的文件中。 这样,即使测试用例集发生更改,也可以直接将最新结果与之前测试运行的结果进行比较。 如果应用程序 SASL 正在运行,其日志也会被 cth_log_redirect 内置钩子打印到当前的次要日志文件中。
次要日志文件的完整名称(即,包括绝对目录路径的文件名)可以在测试用例执行期间读取。它以元组 {tc_logfile,LogFileName} 的形式作为值出现在 Config 列表中(这意味着它也可以被 pre 或 post Common Test Hook 函数读取)。此外,在测试用例开始时,此数据会随事件一起发送到任何已安装的事件处理程序。有关详细信息,请参阅 事件处理部分。
日志文件在测试运行期间持续写入,并且链接总是在测试开始时创建。 因此,只需刷新 HTML 浏览器中的页面即可跟踪测试进度。 但是,统计总数要等到测试完成后才会显示。
日志选项
可以使用启动标志 logopts 指定选项来修改日志记录行为的某些方面。 以下选项可用:
no_src- 在测试运行期间不生成测试套件源代码的 HTML 版本(因此在日志文件系统中不可用)。no_nl-Common Test不会在它从调用(例如,io:format/2)接收到的输出字符串末尾添加换行符(\n),并将其打印到测试用例日志中。
例如,如果测试以以下方式启动:
$ ct_run -suite my_SUITE -logopts no_nl
那么在测试期间通过连续调用 io:format("x") 所做的打印输出,在测试用例日志中显示为:
xxx
而不是每个 x 都打印在新的一行上,这是默认行为。
排序 HTML 表格列
通过单击任何表格的列标题中的名称(例如,“Ok”、“Case”、“Time”等),表格行将按照对于值类型有意义的任何顺序排序(例如,“Ok”或“Time”的数值顺序,以及“Case”的字母顺序)。 排序是通过 JavaScript 代码执行的,该代码会自动插入到 HTML 日志文件中。Common Test 使用 jQuery 库和 tablesorter 插件以及自定义的排序函数来实现此功能。
意外 I/O 日志
测试套件概览页面包含指向意外 I/O 日志的链接。 在此日志中,Common Test 保存使用 ct:log/1,2,3,4,5 和 ct:pal/1,2,3,4,5 进行的打印输出,以及捕获的系统错误和进度报告,这些打印输出无法与特定的测试用例关联,因此无法写入到各个测试用例日志文件中。 例如,如果从外部进程(而不是测试用例进程)进行日志打印输出,或者在 Common Test 未执行测试用例或配置函数的短暂间隔期间,或者在 Common Test 当前正在执行并行测试用例组时,出现错误或进度报告,则会发生这种情况。
测试前和测试后 I/O 日志
Common Test 框架日志页面包含指向测试前和测试后 I/O 日志的链接。 在此日志中,Common Test 保存使用 ct:log/1,2,3,4,5 和 ct:pal/1,2,3,4,5 进行的打印输出,以及捕获的系统错误和进度报告,这些信息发生在测试运行之前和之后。 这方面的示例包括来自 CT 钩子初始化或终止函数的打印输出,或者从 CT 钩子初始化函数启动 OTP 应用程序时生成的进度报告。 另一个示例是由于从 CT 钩子终止函数停止外部应用程序时发生故障而生成的错误报告。 这些示例中的所有信息都最终进入测试前和测试后 I/O 日志。 有关如何将测试运行与外部用户应用程序同步的更多信息,请参阅 Common Test 钩子部分中的 同步部分。
注意
只有当
Common Test正在运行时,使用ct:log/1,2,3,4,5或ct:pal/1,2,3,4,5记录到文件才会生效。 但是,使用ct:pal/1,2,3,4,5的打印输出始终会显示在屏幕上。
删除旧日志
Common Test 可以自动删除旧日志。 这是通过 keep_logs 选项指定的。 此选项的默认值为 all,这意味着不删除任何日志。 如果该值设置为整数 N,则 Common Test 会删除所有 ct_run.<timestamp> 目录,除了最新的 N 个目录。
HTML 样式表
Common Test 使用 HTML 样式表(CSS 文件)来控制测试运行期间生成的 HTML 日志文件的外观。 如果日志文件在您选择的浏览器中未正确显示,或者您更喜欢日志的更原始的(“pre Common Test v1.6”)外观,请使用启动标志/选项:
basic_html这将禁用样式表和 JavaScript 的使用(请参阅 排序 HTML 表格列)。
Common Test 包含一个可选功能,允许用户使用 HTML 样式表自定义打印输出。ct 中打印到测试用例 HTML 日志文件中的函数(log/3,4,5 和 pal/3,4,5)接受 Category 作为第一个参数。 使用此参数,可以指定一个类别,该类别可以映射到 CSS 规则集中命名的 div 选择器。 这很有用,尤其是在根据打印输出的类型(或原因)以不同方式为文本着色时。 假设您希望测试系统配置信息使用一种特定的背景颜色,测试系统状态信息使用另一种背景颜色,最后,测试用例函数检测到的错误使用一种背景颜色。 相应的样式表可以如下所示:
div.sys_config { background:blue }
div.sys_state { background:yellow }
div.error { background:red }Common Test 将 ct:log/3,4,5 或 ct:pal/3,4,5 中的文本打印在命名 div 元素下的嵌套 pre 元素中。 由于 pre 选择器在文件 ct_default.css 中对属性 color、font-family 和 font-size 具有预定义的 CSS 规则,如果用户想要更改任何预定义的属性设置,则必须向用户样式表添加 pre 的新规则。 示例:
div.error pre { color:white }在这里,对于 div.error 打印输出,使用白色文本代替默认的黑色(并且 pre 的其他属性设置不受影响)。
要安装 CSS 文件(Common Test 将定义内联到 HTML 代码中),可以在执行 ct_run 时提供文件名。
示例
$ ct_run -dir $TEST/prog -stylesheet $TEST/styles/test_categories.css通过标志 -stylesheet 安装的 CSS 文件中的类别在全局测试级别上,因为它们可以在测试运行的任何套件中使用。
样式表也可以在每个套件和每个测试用例的基础上安装。
示例
-module(my_SUITE).
...
suite() -> [..., {stylesheet,"suite_categories.css"}, ...].
...
my_testcase(_) ->
...
ct:log(sys_config, "Test node version: ~p", [VersionInfo]),
...
ct:log(sys_state, "Connections: ~p", [ConnectionInfo]),
...
ct:pal(error, "Error ~p detected! Info: ~p", [SomeFault,ErrorInfo]),
ct:fail(SomeFault).如果按照此示例安装样式表,则类别是相关套件私有的。 它们可以被套件中的所有测试用例使用,但不能被其他套件使用。 如果指定了套件私有样式表,则优先使用全局样式表(使用标志 -stylesheet 指定的样式表)。 从测试用例信息函数返回的样式表元组(如上面的 suite/0 返回的元组)也可以返回。 在这种情况下,样式表中指定的类别只能在该特定的测试用例中使用。 测试用例私有样式表优先于套件或全局级别的样式表。
在元组 {stylesheet,CSSFile} 中,如果 CSSFile 是使用路径指定的,例如,"$TEST/styles/categories.css",则会使用此完整名称来查找文件。 但是,如果仅指定文件名,例如,categories.css,则假定 CSS 文件位于套件的 data 目录 data_dir 中。 建议使用后一种用法,因为它与在套件中硬编码路径名相比是可移植的。
在前面的示例中,参数 Category 可以具有值(原子)sys_config(蓝色背景)、sys_state(黄色背景)或 error(红色背景上的白色文本)。
重复测试
您可以命令 Common Test 重复您指定的测试。您可以选择重复测试多次,在特定时间段内重复测试,或者重复测试直到达到特定的停止时间。如果重复由时间控制,则可以指定 Common Test 在超时时采取的操作。 可以让 Common Test 在停止之前执行当前运行中的所有测试,或者在当前测试作业完成后停止。可以通过 ct_run 启动标志或 ct:run:test/1 选项列表参数中的元组来激活重复。 标志(括号中的选项)如下:
-repeat N ({repeat,N}),其中N是一个正整数-duration DurTime ({duration,DurTime}),其中DurTime是持续时间-until StopTime ({until,StopTime}),其中StopTime是完成时间-force_stop ({force_stop,true})-force_stop skip_rest ({force_stop,skip_rest})DurTime- 持续时间指定为HHMMSS格式,例如,-duration 012030或{duration,"012030"},这意味着测试将被执行,并且(如果时间允许)会重复执行,直到超时,超时时间为 1 小时 20 分 30 秒。
StopTime- 结束时间可以指定为HHMMSS格式,然后会被解释为今天的某个时间(或者可能是明天),也可以指定为YYMoMoDDHHMMSS格式,例如,-until 071001120000或{until,"071001120000"}。这意味着测试将被执行,并且(如果时间允许)会重复执行,直到 2007 年 10 月 1 日的 12 点整。
当超时发生时,Common Test 永远不会中止正在进行的测试用例,因为这可能会使被测系统 (SUT) 处于未定义且可能是不良的状态。相反,默认情况下,Common Test 会在停止之前完成当前的测试运行。如果指定了标志 force_stop,则 Common Test 会在当前测试作业完成后停止。如果使用 skip_rest 指定了标志 force_stop,则 Common Test 仅完成当前测试用例,并跳过测试作业中剩余的测试。
注意
由于
Common Test总是至少完成当前的测试用例,因此使用duration或until指定的时间永远不是绝对的。
每次重复测试运行的日志文件都会以正常的 Common Test 方式保存(如前所述)。
Common Test 稍后可能会支持一个可选功能,仅存储重复测试运行的最后(以及可能第一个)一组日志,但目前,如果测试在长时间内重复进行,用户必须小心不要耗尽磁盘空间。
对于作为重复会话一部分的每次测试运行,有关特定测试运行的信息会打印在 Common Test 框架日志中。该信息包括重复次数、剩余时间等等。
示例 1
$ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -duration 001000 -force_stop在这里,测试目录 to1 中的测试套件,然后是 to2 中的测试套件,在一个测试运行中执行。10 分钟后发生超时事件。只要有剩余时间,Common Test 就会重复测试运行(即,从测试 to1 重新开始)。超时后,当当前作业完成时(由于标志 force_stop),Common Test 停止。因此,指定的测试运行可以在测试 to1 之后,在测试 to2 之前中止。
示例 2
$ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -duration 001000 -forces_stop skip_rest在这里,运行与示例 1 中相同的测试,但标志 force_stop 设置为 skip_rest。如果在执行目录 to1 中的测试时发生超时,则会跳过 to1 中剩余的测试用例,并且测试会中止,而不会再次运行 to2 中的测试。如果在执行目录 to2 中的测试时发生超时,则会跳过 to2 中剩余的测试用例,并且测试会中止。
示例 3
$ date
Fri Sep 28 15:00:00 MEST 2007
$ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -until 160000在这里,执行与之前示例中相同的测试运行(并可能重复)。但是,当超时发生时,在 1 小时后,Common Test 会在停止前完成整个测试运行(即,to1 和 to2 始终在同一个测试运行中执行)。
示例 4
$ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -repeat 5在这里,包括 to1 和 to2 测试的测试运行重复执行五次。
注意
不要将此功能与测试用例组的
repeat属性混淆。此处描述的选项用于重复执行整个测试运行,而测试用例组的repeat属性使重复执行套件中的测试用例集成为可能。有关后者的更多信息,请参阅编写测试套件部分中的测试用例组部分。
静默连接
Common Test 中由 ct_telnet、ct_ssh、ct_ftp 等实现的协议处理过程会向测试用例日志输出详细信息。可以使用标志 -silent_connections 关闭此功能。
ct_run -silent_connections [conn_types]在这里,conn_types 指定 SSH、Telnet、FTP、RPC 和/或 SNMP。
示例 1
ct_run ... -silent_connections ssh telnet这将关闭 SSH 和 Telnet 连接的日志记录。
示例 2
ct_run ... -silent_connections这将关闭所有连接类型的日志记录。
致命的通信错误和重新连接尝试始终会打印,即使已对相关连接类型禁止日志记录。但是,发送和接收数据等操作会静默执行。
silent_connections 也可以在测试套件中指定。这可以通过在 suite/0 或测试用例信息列表中返回一个元组 {silent_connections,ConnTypes} 来实现。如果 ConnTypes 是原子列表(SSH、Telnet、FTP、RPC 和/或 SNMP),则会禁止任何相应连接的输出。默认情况下,对于 ConnTypes 中未指定的任何类型的连接,会启用完整日志记录。因此,如果 ConnTypes 是空列表,则会为所有连接启用日志记录。
示例 3
-module(my_SUITE).
suite() -> [..., {silent_connections,[telnet,ssh]}, ...].
...
my_testcase1() ->
[{silent_connections,[ssh]}].
my_testcase1(_) ->
...
my_testcase2(_) ->
...在此示例中,suite/0 告诉 Common Test 抑制来自 Telnet 和 SSH 连接的打印输出。这对所有测试用例都有效。但是,my_testcase1/0 指定对于此测试用例,仅 SSH 应保持静默。结果是 my_testcase1 会在日志中打印 Telnet 信息(如果有),但不会打印 SSH 信息。my_testcase2 不会打印来自任何连接的信息。
silent_connections 也可以使用测试规范中的术语指定(请参阅运行测试和分析结果部分中的测试规范部分)。通过启动标志/选项 silent_connections 提供的连接会与测试规范中列出的任何连接合并。
启动标志/选项 silent_connections 和测试规范术语将覆盖测试套件内部信息函数所做的任何设置。
注意
在当前的
Common Test版本中,silent_connections功能仅适用于 Telnet 和 SSH 连接。未来版本的Common Test可以添加对其他连接类型的支持。