本主题说明如何添加用户自定义的桩函数,以替代在测试过程中无法访问(或不想访问)的资源调用,以及如何修改自动生成的桩函数。有关桩函数的背景信息及其如何帮助您在开发过程中测试软件,请参阅桩函数。
章节目录:
|
桩函数视图基于最近一次运行的单元测试测试配置提供有关桩函数配置的详细信息。您可以通过为缺少的符号添加用户自定义的桩函数或自动生成桩函数来修改配置。
选择 Parasoft> 显示视图> 桩函数以访问桩函数视图。
第一次打开桩函数视图时,除了显示“没有收集符号数据”消息之外,视图内容将为空。 |
桩函数视图中包括以下内容:
桩函数视图中的内容将根据单元测试期间收集的数据以及在桩函数视图中的特定操作(即创建用户桩函数,自动生成桩函数)进行更新。需注意,桩函数视图内容不会因用户进行的外部操作更新(手动添加/删除桩函数等)。
桩函数视图能提供最近使用的桩函数配置的信息以及其他可用(但未使用)的定义。例如,桩函数视图能显示具有用户定义桩函数的函数现有的原始定义信息。对于这些定义类型,(未使用)
将显示在“定义”列,例如:原始 (未使用)
。如需在桩函数视图中隐藏这类定义,可点击桩函数视图工具栏中的筛选按钮,然后勾选隐藏未使用的定义选项。
如果同一个函数具有多个桩函数定义(例如某个函数具有两个用户桩函数定义),将会在桩函数视图中同时显示。在该桩函数图标上将标注错误图标并在“定义”列显示 (冲突)
,例如:用户
(冲突
)。
收集或刷新符号数据的步骤:
桩函数视图允许指定自动生成的桩函数的保存位置,以及启用或禁用所需的动态桩函数配置模式(请参阅动态桩函数配置):
通过创建和编辑向导生成的桩函数文件来添加用户定义的桩函数:
桩函数目录可以位于项目中的任何位置。默认情况下,C/C++test 期望桩函数存储在项目的桩函数目录的子目录中。但是,您可以使用其他位置,只要相应修改测试配置的使用从以下位置找到的文件中的额外符号(执行> 符号 选项卡)即可。
在项目树中选择您的桩函数目录, 然后选择 C++test> 向导> 创建新的用户桩函数文件。将打开一个向导。在该向导的函数表中,选择您想要为其创建桩函数的函数,然后点击下一步。
|
#include
指令。#include
指令。如需为给定的库中所有函数快速添加桩函数,可按照位置对表格进行排序,从而更轻松地从该库中选择所有函数。 |
将用户定义的桩函数添加到一个空的桩函数文件中的步骤:
在项目树中选择您的桩函数目录,然后选择 C++test> 向导> 创建新的用户桩函数文件。将打开一个向导。 在该向导的函数表中,选择您想要为其创建桩函数的函数,然后点击下一步。
通过键入 stub,将光标移至“stub”的“b”后面,按下 Ctrl + 空格,然后选择适当的模板(用于 C 或 C++ 函数的标准桩函数模板或构造函数/析构函数桩函数)来创建桩函数模板。
通过右键点击您想要添加桩函数模板的代码,然后选择 Parasoft> C++test> 插入 Snippet> [适当的模板类型](C 或 C++ 函数的标准桩函数模板或构造函数/析构函数桩函数模板)来创建桩函数模板。
您可以使用 Tab 键在 ret_type、范围、名称和参数之间移动。 |
8. 输入桩函数主体/定义。如用户定义的桩函数的 C++test API 函数中所述,用户定义的桩函数可以与 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++test API 函数)。
自定义这类桩函数或者桩函数模板的步骤:
需注意,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 标志),如指定自定义编译器设置和链接器选项中所述。