该主题不但解释了如何修改自动生成的桩函数,而且解释如何添加用户定义的桩函数来代替在测试过程中无法(或不想)获得的外部资源调用。有关桩函数及其在开发过程中如何帮助您测试软件的背景信息,请参见桩函数。
各节内容包括:
|
【桩函数】视图基于最近一次运行的单元测试【测试配置】提供有关桩函数配置的详细信息。它允许您通过添加用户定义的桩函数或为丢失的符号自动生成桩函数来修改配置。
选择 Parasoft> 显示视图> 桩函数以访问桩函数视图。
当第一次打开桩函数视图时将显示为空,并且将显示“未收集符号数据 ”的消息。 |
桩函数视图中包括以下栏目:
桩函数视图中的内容将根据单元测试期间收集的数据进行更新,并响应于桩函数视图中可用的特定操作(即创建用户桩函数,生成自动桩函数)。值得指出的是,用户进行的外部操作的响应并不会使桩函数视图更新内容(手动添加/删除桩函数等)。
桩函数视图能提供最近使用的桩函数配置的信息以及其它可用(但未使用)定义。例如,桩函数视图能显示用户桩函数定义的函数的原始定义信息。这些定义类型将显示 (未使用)
在 “定义”这一栏中,例如:初始 (未使用)
。如需在桩函数视图中隐藏这些定义,单击桩函数
视图工具栏上的筛选按钮,然后勾选隐藏未使用定义选项。
如果同一个函数具有多个桩函数定义 (例如某个函数具有两个用户桩函数定义),那么桩函数视图中将会显示两个存根。在该桩函数图标上标注一个错误图标并在 “定义 ”这一栏中显示冲突
, 例如:用户
(冲突
)。
如需收集或刷新符号数据:
桩函数视图允许您指定自动生成的桩函数的保存位置,以及启用或禁用所需的动态桩函数配置模式(请参见动态桩函数配置):
通过创建和编辑向导生成的桩函数文件来添加用户定义的桩函数:
桩函数目录可以位于项目中的任何位置。默认情况下,C/C++test 期望桩函数存储在项目的桩函数目录的子目录中。然而,您可以使用其他位置,只要您相应地修改【测试配置】的 使用设置中找到的文件中的多余符号 (在 执行> 符号 选项卡中)即可。
在项目树中选择您的桩函数目录,然后 然后选择C++test> 向导> 创建新用户桩函数文件 。将打开一个向导。在该向导的功能表中,选择您想要为其创建桩函数的函数,然后单击下一步。
|
#include
指令。#include
指令。要为给定库中的所有函数快速添加桩函数,请按位置对表进行排序,以便更轻松地从该库中选择所有函数。 |
要将用户定义的桩函数添加到一个空的桩函数文件中:
在项目树中选择您的桩函数目录,然后选择C++test> 向导> 创建新用户桩函数文件 。将打开一个向导。 在该向导的功能表中,选择您想要为其创建桩函数的函数,然后单击下一步。
通过键入桩函数,将光标紧接在“桩函数”中的“ b”之后,按Ctrl + 空格,然后选择适当的模板(用于 C 或 C++ 函数的标准桩函数模板或构造函数/析构函数桩函数)来创建桩函数模板)。
通过右键单击您想要添加代码的代码来创建桩函数模板,然后选择Parasoft> C++test> 插入代码段> [适当的 template_type](C 或 C++ 函数的标准桩函数模板或构造函数/析构函数桩函数模板)。
您可以使用Tab 键在 ret_type、作用域、名称和参数之间移动。 |
8。输入桩函数数体/定义。如 C++test API Functions for User-Defined Stubs所述,用户定义的桩函数可以与 C/C++test API 函数交互。
9。保存修改后的文件。
请务必把测试配置的桩函数模式设置为 生成所有调用 函数的桩函数步骤为:
|
当插桩虚函数调用时,确定要在编译时候为一个给定指针或引用所指向类的函数创建一个桩函数。例如,在下列的代码中:只要为 Base::doSth() 方法创建了一个桩函数,标记有(*) 的调用将会被插桩。
被指针指向对象的实际运行时类型并不重要。 |
创建操作符的用户桩函数,使用下面的桩函数:
操作符 | 函数名称 |
---|---|
new | CppTest_Stub_operator_new |
delete | CppTest_Stub_operator_delete |
new[] | CppTest_Stub_operator_array_new |
delete[] | CppTest_Stub_operator_array_delete |
+ | CppTest_Stub_operator_plus |
- | CppTest_Stub_operator_minus |
* | CppTest_Stub_operator_star |
/ | CppTest_Stub_operator_divide |
% | CppTest_Stub_operator_remainder |
^ | CppTest_Stub_operator_excl_or |
& | CppTest_Stub_operator_ampersand |
| | CppTest_Stub_operator_or |
~ | CppTest_Stub_operator_or |
! | CppTest_Stub_operator_not |
= | CppTest_Stub_operator_assign |
< | CppTest_Stub_operator_lt |
> | CppTest_Stub_operator_gt |
+= | CppTest_Stub_operator_plus_assign |
-= | CppTest_Stub_operator_minus_assign |
*= | CppTest_Stub_operator_times_assign |
/= | CppTest_Stub_operator_divide_assign |
%= | CppTest_Stub_operator_remainder_assign |
^= | CppTest_Stub_operator_excl_or_assign |
&= | CppTest_Stub_operator_and_assign |
|= | CppTest_Stub_operator_or_assign |
<< | CppTest_Stub_operator_shift_left |
>> | CppTest_Stub_operator_shift_right |
>>= | CppTest_Stub_operator_shift_right_assign |
<<= | CppTest_Stub_operator_shift_left_assign |
== | CppTest_Stub_operator_eq |
!= | CppTest_Stub_operator_ne |
<= | CppTest_Stub_operator_le |
>= | CppTest_Stub_operator_ge |
&& | CppTest_Stub_operator_and_and |
|| | CppTest_Stub_operator_or_or |
++ | CppTest_Stub_operator_plus_plus |
-- | CppTest_Stub_operator_minus_minus |
->* | CppTest_Stub_operator_arrow_star |
-> | CppTest_Stub_operator_arrow |
() | CppTest_Stub_operator_function_call |
[] | CppTest_Stub_operator_subscript |
<? | CppTest_Stub_operator_gnu_min |
>? | CppTest_Stub_operator_gnu_max |
, | CppTest_Stub_operator_comma |
右键单击表中的函数,然后从快捷菜单中选择 生成自动桩函数 为缺少定义的符号生成自动桩函数文件。将创建一个自动桩函数文件,并在编辑器中将其打开。C/C++test 将自动添加适当的定义和必需的 #include 指令。
您还可以同时为多个符号创建自动桩函数:
您也可以为没有可用定义的符号创建自动桩函数。对于其他符号,请改用【用户桩函数】。
自动桩函数具有最低优先级。如果有其他定义可用,则不会使用自动桩函数。
如上所述,C/C++test 可用于为缺失的函数和变量定义自动生成可自定义的桩函数。
自动生成的桩函数具有与用户定义的桩函数相同的功能,但是带有CppTest_Auto_Stub_
前缀(而不是CppTest_Stub_
前缀)标记。这使您可以为作用域中的同一函数使用多个桩函数。
如果 C/C++test 无法自动生成完整的桩函数定义,它将创建一个您可以自定义的桩函数模板(通过输入适当的 return 语句、添加 include 指令等)。完整桩函数之前,桩函数模板将保存在桩函数文件中。
仅当没有其他定义(用户桩函数或原始桩函数)可用时,才使用自动生成的桩函数。
您可以通过动态桩函数配置 API(请参见动态桩函数配置)或使用【测试用例编辑器存根】步骤(请参见使用步骤)来轻松配置自动生成的桩函数。
在极少数情况下,动态桩函数配置 API 或测试用例编辑器不足,您可以使用自定义逻辑实现完全替换生成的桩函数主体,例如 CppTest_IsCurrentTestCase
桩函数(请参阅 “用于用户定义桩函数的 C/C++test API 函数 C++test API Functions for User-Defined Stubs)。
要自定义这些桩函数或者桩函数模板:
请注意,C++test 的桩函数(构造函数桩函数除外)采用与原始函数相同的值。
安全定义会自动生成,以替换“危险”函数。安全定义可用于大多数系统 I/O 例程(rmdir()
, remove()
, rename()
,等)。如果使用了安全的定义,则不会调用原始文件—即使它们可用。我们建议您使用安全定义(如果可用)。使用这些定义可以防止在单元测试期间出现问题。这些桩函数不能被修改。
如果您不想使用自动生成的安全定义,请从【测试配置】的执行> 符号 选项卡中的 使用文件中的其他符号 字段中删除${cpptest:cfg_dir}/safestubs
。
如果要禁用特定函数的安全桩函数的使用(并使用原始定义),请编写将用作原始函数调用包装程序的用户桩函数。示例:
int CppTest_Stub_mkdir(const char* p) { return mkdir(p); } |
void CppTest_Assert(bool test, const char * message) |
该函数的工作方式类似于标准断言函数。只要 "test” 参数的值为 false,就会停止执行测试用例。将出现“用户定义的断言失败”报文,作为测试用例结果。此外,"message” 参数的值将显示为详细的故障说明,以及位置和堆栈跟踪详细信息。
void CppTest_Break() |
此函数使您可以无条件停止测试用例的执行。以这种方式停止的测试用例将导致“用户定义的中断已调用”报文。此外,还提供了位置和堆栈跟踪信息。
bool CppTest_IsCurrentTestCase(const char* id; |
该函数允许您查询当前执行的测试用例。如果指定的 id 等于当前执行的测试用例的名称,它将返回true
,否则将返回false
。此函数对于使用基于外部函数调用的条件语句的函数很有用。参见示例由测试用例驱动的存根函数 。
bool CPPTEST_DS_HAS_COLUMN(const char* name) |
您可以用此函数查询用户/自动桩函数中的数据源列。请注意,数据源是特定于测试用例的,因此只有在给定测试用例的上下文中调用桩函数时,数据才可用(在全局初始化期间调用桩函数时,数据不可用)。
const char* CppTest_GetCurrentTestCaseName(); const char* CppTest_GetCurrentTestSuiteName(); |
这些函数获得当前执行的测试用例和测试套件的名称。使用这些功能,您可以编写适用于特定测试套件中特定测试用例的桩函数。
有关示例,请参见由测试用例驱动的存根函数。
您配置用于在 C/C++test 中使用的任何数据源可以在桩函数中使用。
要配置数据源,请使用添加数据源中提供的说明。
要在桩函数中使用数据源,请使用 bool CPPTEST_DS_HAS_COLUMN(const char * name)API 函数。您可以按以下方式查询桩函数中的数据源列:
int CppTest_Stub_goo (void) { if (CPPTEST_DS_HAS_COLUMN("stub_goo_return")) { return CPPTEST_DS_GET_INTEGER("stub_goo_return"); } else { return 0; // Data Source not available } } |
请注意,数据源是特定于测试用例的,因此只有在给定测试用例的上下文中调用桩函数时,数据才可用(在全局初始化期间调用桩函数时,数据不可用)。
此 bool CPPTEST_DS_HAS_COLUMN(const char* name)
宏也可以与其他 API 函数结合使用—例如 CppTest_isCurrentTestCase()。
请参见动态桩函数配置。
C/C++test 允许您创建可由当前执行的测试用例“驱动”的桩函数。C/C++test API 提供以下功能:
bool CppTest_IsCurrentTestCase(const char* id); |
可以在“用户桩函数”定义中使用此函数来查询当前执行的测试用例。如果指定的 id 等于当前执行的测试用例的名称,它将返回true
,否则将返回false
。
此函数对于使用基于外部函数调用的条件语句的函数很有用。示例:
void foo() { if (goo() == 1) { //code } else { //code } } |
要实现 100% 的行覆盖率,您可以为foo()
函数创建两个测试用例,然后为goo()
函数创建一个用户存根:
int ::CppTest_Stub_goo() { if (CppTest_IsCurrentTestCase("TestCase1")) { return 1; } else { return 0; } } |
现在,由测试用例“驱动”该存根。在此示例中,返回值取决于当前执行的测试用例:它取决于TestCase1
测试用例。对于TestCase1
,它将返回 1,对于任何其他测试用例,它将返回 0。这样,您可以实现foo()
函数的 100% 覆盖率。
其他对编写由测试用例驱动的存根有用的 API 函数是 const char* CppTest_GetCurrentTestCaseName()
; 和 const char* CppTest_GetCurrentTestSuiteName();。这些函数获得当前执行的测试用例和测试套件的名称。使用这些功能,您可以编写适用于特定测试套件中特定测试用例的桩函数。例如,以下桩函数对于名称包含“ nomemory”且来自“ AllocTestSuite”测试套件的所有测试用例的行为均不同:
#include <string> #include <stdlib.h> EXTERN_C_LINKAGE void* CppTest_Stub_malloc(size_t size) { std::string testSuiteName = CppTest_GetCurrentTestSuiteName(); std::string testCaseName = CppTest_GetCurrentTestCaseName(); if ((testSuiteName == "AllocTestSuite") && (testCaseName.find("nomemory") != std::string::npos)) { // Simulate no memory situation. return 0; } return malloc(size); } |
您可以为每个用户定义的桩函数文件设置自定义编译器选项(例如,使用指定的 C/C++test 标志),如 指定自定义编译器设置和链接器选项中所述。