本主题说明如何使用桩函数回调进行动态桩函数配置。
章节目录:
说明
桩函数回调提供了一种强大的机制,用于编程测试用例特定的桩函数逻辑。对于每个测试用例,您可以定义在测试用例执行期间每次调用桩函数时要执行的特定桩函数行为。C++test 会自动生成代码来运行测试用例特定的桩函数逻辑,因此您无需修改主桩函数定义。
您可以通过以下方式之一配置桩函数回调:
- 使用测试用例编辑器的界面(请参阅在测试用例编辑器中配置桩函数行为)
- 手动修改测试用例源代码(请参阅在源代码中配置桩函数行为)
测试用例编辑器会自动生成桩函数回调框架的可配置组件,显示有关桩函数配置的建议,并在创建桩函数回调后对其进行注册。
桩函数回调快速指南
在创建桩函数之前,应确保在桩函数视图中(请参阅在桩函数视图中配置桩函数选项)或测试配置中(请参阅执行选项卡设置 - 定义测试的执行方式)勾选启用桩函数回调选项。
在测试用例编辑器中配置桩函数行为
- 使用自动生成桩函数或创建用户桩函数选项为您要配置的函数创建桩函数(请参阅添加和修改桩函数)。
- 在测试用例编辑器中打开测试用例。
- 添加一个新的桩函数配置步骤,并指定您要配置的函数。
- 使用可用的操作和输入/输出参数定义桩函数逻辑。
- 执行您的测试用例。运行测试用例时,将在调用桩函数时执行在桩函数配置步骤中定义的自定义桩函数逻辑。
有关使用测试用例编辑器配置桩函数行为的更多详细信息,请参阅使用测试用例编辑器添加测试套件和测试用例 - 配置桩函数行为。
在源代码中配置桩函数行为
使用自动生成桩函数或创建用户桩函数选项为您要配置的函数创建桩函数(请参阅添加和修改桩函数)。
- 在代码编辑器中打开测试套件文件。
将桩函数回调函数添加到测试套件文件。
使用可用的输入/输出参数在桩函数回调函数中定义桩函数逻辑。
导航到测试用例定义,并使用以下宏注册桩函数回调:
CPPTEST_REGISTER_STUB_CALLBACK(<STUB_ID>, &<STUB_CALLBACK_NAME>)
在调用被测函数之前,请确保已注册回调。
- 执行您的测试用例。执行测试用例时,将在调用桩函数时执行您配置的桩函数逻辑。
有关桩函数回调函数和桩函数回调注册的更多信息,请参阅桩函数回调详细信息。
桩函数回调详细信息
桩函数回调框架包含以下组件:
- 桩函数回调函数:用户定义的函数,用于实现各个桩函数的自定义桩函数逻辑。
- 桩函数回调注册:用于激活桩函数回调函数的机制,通常放置在测试用例中。
- 桩函数回调执行:桩函数回调函数的调用,内置在 C++test 的桩函数定义中。
桩函数回调函数
桩函数回调函数是用户定义的 C/C++ 函数,其中包含要在调用相关桩函数时执行的逻辑。它具有从桩函数/桩函数函数的签名派生的唯一签名:
void <CALLBACK_NAME>(CppTest_StubCallInfo* stubCallInfo, <STUB_IN_OUT_PARAMETERS>)
桩函数回调函数的接口包括被打桩的函数的所有输入/输出参数(<STUB_IN_OUT_PARAMETERS>)
,以及其他帮助参数(stubCallInfo)
。您还可以自定义桩函数回调函数的名称(<CALLBACK_NAME>)
。
以下示例演示了如何为 int processValues(int i, int j);
函数自定义桩函数回调函数:
void CppTest_StubCallback_processValues_case_1(CppTest_StubCallInfo* stubCallInfo, int* __return, int i, int j) { // checking input value CPPTEST_ASSERT_INTEGER_EQUAL(-1, i); // modifying return value *__return = 150; }
我们建议您将桩函数回调函数以及相关的测试用例定义保留在同一测试套件文件中。
您可以将桩函数回调函数定义为测试套件类的全局函数或静态成员函数(仅限 C++)。当启用“访问私有成员代码”插桩功能时,这允许桩函数回调函数访问被测代码的私有类成员。
使用以下桩函数回调接口,可以用任何有效的 C 或 C++ 代码表示自定义桩函数逻辑:
- 被打桩的函数参数 - 用作桩函数回调函数的参数
- 被打桩的函数返回值 - 用作指向返回值容器的“
__return
”指针 - 被打桩的函数类对象 - 用作指向类对象的“
__this
”指针 - 桩函数回调函数的当前调用数量 - 用作“
int stubCallInfo->callNo
”变量
此外,您可以在桩函数回调函数中使用全局变量或全局函数,包括 C++test 单元测试 API(例如,CPPTEST_ASSERT
和 CPPTEST_REPORT)
。如果被打桩的函数的原始定义可用,也可以从桩函数回调函数中执行。
您可以在测试套件文件中定义多个桩函数回调函数 - 它们能够与相同的桩函数或多个不同的桩函数对应。
一个桩函数回调函数可以在单个测试用例中使用,也可以在许多不同的测试用例中共享。
一个桩函数一次只能激活一个桩函数回调函数(请参阅桩函数回调注册)。
桩函数回调注册
桩函数回调函数必须进行注册才能有效。通常在第一次调用被打桩的函数之前,在相关的测试用例中注册桩函数回调函数。在大多数情况下,应在被测函数的调用之前进行注册。
使用以下 API 调用来注册桩函数回调函数:
CPPTEST_REGISTER_STUB_CALLBACK(<STUB_ID>, &<STUB_CALLBACK_NAME>)
其中:
<STUB_ID>
是唯一的桩函数标识符(请参阅桩函数回调执行)<STUB_CALLBACK_NAME>
是用户定义的桩函数回调函数的名称
示例:
CPPTEST_REGISTER_STUB_CALLBACK("processValues", &CppTest_StubCallback_processValues_case_1);
您可以在多个测试用例中注册相同的桩函数回调函数,以便每个测试用例针对给定的桩函数使用相同的自定义逻辑。同样,可以在一个测试用例中注册不同的桩函数回调函数(针对不同的桩函数)。这使测试用例能够为多个桩函数提供自定义行为。在任何给定的时间点,针对某个特定的桩函数,只能有一个活跃的桩函数回调函数。
桩函数回调函数的生命周期:
- 激活:使用
CPPTEST_REGISTER_STUB_CALLBACK(<STUB_ID>, &<STUB_CALLBACK_NAME>)
注册 - 停用:测试用例结束或为同一桩函数注册了另一个桩函数回调函数(相同的
<STUB_ID>
)
示例:
void test_case_1() { ... // CppTest_StubCallback_foo activation: CPPTEST_REGISTER_STUB_CALLBACK("foo", &CppTest_StubCallback_foo); // CppTest_StubCallback_bar activation: CPPTEST_REGISTER_STUB_CALLBACK("bar", &CppTest_StubCallback_bar); ... callToFunctionUnderTest(); // will call foo() and bar() ... } // end of test case: CppTest_StubCallback_foo deactivation, CppTest_StubCallback_bar deactivation void test_case_2() { ... // CppTest_StubCallback_foo activation: CPPTEST_REGISTER_STUB_CALLBACK("foo", &CppTest_StubCallback_foo); // CppTest_StubCallback_bar activation: CPPTEST_REGISTER_STUB_CALLBACK("bar", &CppTest_StubCallback_bar); ... callToFunctionUnderTest(); // will call foo() and bar() ... // CppTest_StubCallback_foo_1 activation, CppTest_StubCallback_foo deactivation CPPTEST_REGISTER_STUB_CALLBACK("foo", &CppTest_StubCallback_foo_1); } // end of test case: CppTest_StubCallback_foo_1 deactivation, CppTest_StubCallback_bar deactivation
桩函数回调执行
如果定义并注册了桩函数回调函数,则每次调用相应的桩函数都会执行该函数。回调函数通过
, 来调用,如果在创建桩函数时启用了“启用桩函数回调”选项(默认情况下启用此选项),则该函数会自动添加到桩函数定义中。CPPTEST_STUB_INVOKE_CALLBACK()
示例:
/** Auto-generated stub definition for function: int processValues(int, int) */ int processValues (int i, int j) ; int CppTest_Auto_Stub_processValues (int i, int j) { CPPTEST_STUB_CALLED("processValues"); int __return = 0; /** * This section enables Dynamic Stub Configuration with Stub Callbacks. * * IMPORTANT: THIS COMMENT BLOCK SHOULD NOT BE DELETED OR MODIFIED * * 1. Define stub callback function in test suite file - use the following signature: * void CppTest_StubCallback_SomeName(CppTest_StubCallInfo* stubCallInfo, int* __return, int i, int j) * * 2. Register stub callback in test case function - use the following code: * CPPTEST_REGISTER_STUB_CALLBACK("processValues", &CppTest_StubCallback_SomeName); * * 3. Fill out the body of the stub callback function according to intent. * The following line may be used to call original function inside stub callback: * *__return = ::processValues(i, j); */ if (CPPTEST_STUB_HAS_CALLBACK()) { CPPTEST_STUB_CALLBACK_PARAMS(int* __return, int i, int j); CPPTEST_STUB_INVOKE_CALLBACK(&__return, i, j); } else { /* You can put additional stub logic here. */ } return __return; }
自动添加到桩函数定义的桩函数回调调用部分包含桩函数回调函数的默认签名和示例性的桩函数回调注册行,当您手动配置桩函数回调时,可以将其复制到测试套件文件中。
桩函数标识符
用于桩函数回调注册和桩函数回调执行的桩函数标识符必须唯一,以标识各个桩函数。默认情况下, C++test 生成的桩函数按照以下模式使用标识符:
[<parent::>]<function_name>
仅当被打桩的函数是类或名称空间成员时,才添加 <parent::>
前缀。仅添加直接父级。对于模板类方法、父级名称中会省略模板参数。
对重载函数使用桩函数回调
C/C++test 对函数的所有重载版本使用相同的函数标识符(请参阅桩函数标识符)。在对重载函数使用桩函数回调之前,需更新用于桩函数回调执行代码(CPPTEST_STUB_CALLED()
)和桩函数回调注册的标识符,以确保其唯一性。这样即可区分重载函数/方法的桩函数。
示例:
用于 foo() 的所有重载版本的默认标识符:
CPPTEST_STUB_CALLED("foo");
foo(int) 更新后的标识符:
CPPTEST_STUB_CALLED("foo_int")
创建调用原始函数的桩函数
如果没有在测试用例中注册测试用例特定的桩函数回调函数,则 C/C++test 允许生成调用原始函数的桩函数。为此,需在桩函数视图(请参阅在桩函数视图中的配置桩函数选项)或测试配置(请参阅执行选项卡设置 - 定义测试的执行方式)中启用以下选项:
- 启用桩函数回调
- 插入对原函数的调用
启用这些选项后,C/C++test 将应用以下桩函数策略:
- 如果针对当前执行的测试注册了测试案例特定的桩函数回调,则桩函数回调函数将用于对原始函数打桩。
- 如果没有为当前执行的测试注册测试用例特定的桩函数回调,则桩函数将调用原始函数。因此,在测试用例执行期间将调用原始函数,而桩函数则充当“代理”角色。
- 如果没有为当前执行的测试注册测试用例特定的桩函数回调,并且原始函数不可用,C/C++test 将使用您在桩函数定义中指定的自定义逻辑。
示例:
以下情况不支持调用原始函数:
- 构造函数
- 带省略号的函数
- 纯虚函数
- 具有内部链接的功能
- 具有推导的返回类型并且在任何标头中都不可见的函数