在创建桩函数之前,桩函数API要求在桩函数视图中启用 启用桩函数 API 选项(请参见 在桩函数视图中配置桩函数选项) 或直接在测试配置中启用 (请参见 执行选项卡设置 - 定义如何执行测试)。

您可以针对与测试用例相关的特定需求动态配置桩函数行为。桩函数配置是使用专用 API 在源代码中定义的。您可以在测试套件设置/拆卸,测试案例设置/拆卸以及测试案例内部指定桩函数配置。执行完测试用例后,将立即删除在测试用例内部创建的桩函数配置。同样,在测试用例和测试套件 setup() 函数内部指定了桩函数配置后,将在执行各自的teardown() 函数后将其删除。

用户定义的桩函数行为在内部由“触发对象”表示。定义触发器对象时,它会累积指定的一组操作、值和参数引用。触发器在执行时执行已定义的操作。在定义触发对象时,将使用名称引用的实参来补充触发对象的执行。下表显示了用于指定和执行桩函数行为定义(触发对象)的宏。 

定义
CPPTEST_ON_CALL(func-id)<definition>定义桩函数行为(创建触发器对象)。通常在测试用例主体中指定。
CPPTEST_AFTER_CALL(func-id)<definition>定义仅在对桩函数进行原始功能/方法调用(创建触发器对象)之后执行的附加(可选)桩函数行为。通常在测试用例主体中指定。
CPPTEST_ACTUAL_CALL(func-id)<args>执行桩函数的已定义行为。始终位于桩函数内部。
CPPTEST_ACTUAL_AFTER_CALL(func-id)<args>为原始函数/方法调用后处理执行已定义的行为。始终位于存根内部。
  • 定义: 定义预期桩函数行为的桩函数 API 调用序列
  • args: 传递给桩函数行为执行的参数序列
  • func-id: 用于标识桩函数的字符串。默认情况下, C++test 生成的桩函数按照以下模式使用标识符:<parent::><function_name>.仅当函数/方法是类或名称空间成员时,才添加 parent:: 前缀。仅添加直接父级。对于模板类方法、父名称中省略了模板参数。

C++test 对函数的所有重载版本使用相同的桩函数函数标识符。如果必要,可以通过修改桩函数定义为每个重载的桩函数函数引入唯一的桩函数函数标识符。这将使您能够区分重载的函数/方法的桩函数。

下表显示了与测试用例 (C++ API) 一起使用的桩函数主体的高度简化示例,以可视化数据和控制流。

测试用例定义桩函数定义
void testCase1 
{
 CPPTEST_ON_CALL("foo")
   If().Arg("param1").Equal(0).Fail("Problem").   
   Arg("__return").Assign(3);
 
   int __return =  goo(a);
   CPPTEST_ASSERT(__return == 5);
}
 
int foo(int param1, int param2);
int CppTest_Stub_foo (int param1, int param2){ 
  int __return = 0;   
  int __callOrig = 0;
 
  CPPTEST_ACTUAL_CALL("foo").
    WithArg("param1", ByRef(param1)).     
    WithArg("param2", ByRef(param2)).     
    WithArg("__return", ByRef(__return)).     
    WithArg("__callOrig", ByRef(__callOrig)).     
    End();
 
  if (_callOrig) {
    int _return = foo(a);
    CPPTEST_ACTUAL_AFTER_CALL("foo").
	WithArg("a", ByRef(a)).
	WithArg("__return", ByRef(__return)). 
	End();
    return __return;
  }
  return __return; 
}

在此示例中,函数foo()的桩函数执行以下行为:

  • param1 的值测试为 0 如果parasm1 等于 0则报告问题报文。
  • __return 触发器参数分配 3的整数值,该参数表示从桩函数返回的变量。

桩函数行为定义可以包括分配、逻辑运算、关系运算、算术运算和执行回调函数。有关桩函数行为定义语法的详细说明,请参见 桩函数 API 参考。C 和 C++ 语言的桩函数 API 有一些区别。语法说明和提供的示例中突出显示了所有差异。以下差异特别重要:

  • 定义桩函数行为时用于调用方法/函数链的运算符对于 C 和 C++ 是不同的。API 的 C++ 版本使用点运算符(.),而 C 版本使用箭头运算符 (→)
  • 在桩函数行为定义中指定值时,C 版本需要使用类型专用的方法来传递值,而 C++ 版本则允许使用单个重载方法。

上例中的桩函数行为定义使用 C 语言时具有以下形式:

CPPTEST_ON_CALL("foo")->If()->Arg("param1")->Equal()->Int(0)->Assert("Problem")-> Arg("__return")->Assign(3);

请注意使用箭头运算符(->)代替点运算符(.),以及使用Int()函数调用传递整数值。

为桩函数行为定义指定参数

您可以按名称或计算结果为参数名称的表达式在桩函数行为定义中指定参数。在桩函数内部执行已定义的行为时,将提供命名实参的实际值,如上一示例中对param1__return所示。

下表显示了用于指定桩函数行为定义的参数的 API 方法:

API 方法:说明
Arg(const char *argName)

参数名称作为字符串提供。CPPTEST_ACTUAL_CALL and CPPTEST_ACTUAL_AFTER_CALL 中使用的名称必须完全匹配。

示例:

[TestCase] CPPTEST_ON_CALL("foo").Arg("param1").Assign(0);
[Stub] CPPTEST_ACTUAL_CALL(“foo”).WithArg(“param1”, ByRef(“param1”).End();

ArgByExpr()

该参数通过评估随后的表达式确定,该表达式应具有 const char * 类型。

示例:

CPPTEST_ON_CALL(“foo”).ArgByExpr().Arg(“argName”).Assign(0);

假设{Arg(“arg_name”)}将在桩函数执行期间求值为“char *”类型值,该值将用作参数名称,因此:

{ArgByExpr().Arg(“argName”)}=>{Arg(<value of Arg(“argName”)>)}

C++test 生成的桩函数对默认参数使用以下命名约定:

  • 桩函数参数(反映桩函数函数/方法的参数)可以按原始函数/方法声明中指定的名称使用
  • 用于从桩函数返回值的变量在名称__return下可用
  • 名称__callOrig提供了用作将调用重定向到原始符号定义的条件的变量

指定桩函数行为定义的值

可以“按值”或“按引用”提供操作的值,这在功能上与 C/C++ 中的概念相同。当出现左值行为时,必须通过对ByRef()函数的调用来包装传递的值。在期望右值语义的情况下,不必使用ByRef()来包装值。

如果期望进行赋值或将实际值的评估推迟到执行桩函数,则通常最好通过引用传递变量。请参见下面的示例:

extern int global;
[By Value] CPPTEST_ON_CALL("foo").Arg("param1").Assign(global);
[By Reference] CPPTEST_ON_CALL("foo").Arg("param1").Assign(ByRef(global));

在第一个示例中,将在处理CPPTEST_ON_CALL 定义时复制分配给param1 的值。执行赋值时,不会反映对全局变量的实际值的任何进一步更改。

在第二个示例中,在处理CPPTEST_ON_CALL定义时不会复制分配给param1 的值。取而代之的是,将存储对它的“引用”(作为指针),并在执行分配时获取 global的实际值。

在 C 和 C++ 模式下为桩函数行为定义指定值时,存在重要区别。在 C 模式中,值规范必须总是用特定类型的函数包装。示例:

CPPTEST_ON_CALL("foo")->Arg("param2")->Assign()->Float(1.5);
CPPTEST_ON_CALL("foo")->Arg("param3")->Assign()->VoidPtr(ptr);

为所有简单类型提供包装函数。完整的列表,请参见 桩函数 API 参考。在 C++ 模式下,无需包装函数即可提供值。在需要值指定的所有情况下,您都可以使用通用的“值”函数,该函数对于所有简单类型都是重载的。对于更简单的表示法,“操作符方法”的重载版本直接接受该值。在下面的示例中显示:

CPPTEST_ON_CALL("foo").Arg("param1").Assign().Value(1); 
CPPTEST_ON_CALL("foo").Arg("param1").Assign().Value(1.77);
CPPTEST_ON_CALL("foo").If().Arg("p1").Greater().Value(1.77).Arg("p2").Assign(0);

使用重载的“操作符”方法的简单表示法:

CPPTEST_ON_CALL("foo").Arg("param1").Assign(1);
CPPTEST_ON_CALL("foo").Arg("param1").Assign(1.77);
CPPTEST_ON_CALL("foo"().Arg("p1").Greater(1.77).Arg("p2").Assign(0);

下一节讨论典型的用例或桩函数 API。

示例 1: 调用原始函数

您可以从桩函数定义中调用原始函数/方法定义(假设原始定义在测试二进制文件中可用)。默认情况下,禁用对原始符号的调用。要启用此功能,请为命名参数__callOrig分配一个非零值。将此参数赋值为 0 以禁用。

如果启用了调用原始函数的功能,但不存在原始定义,则将在没有停止条件的情况下递归调用桩函数。这将导致堆栈溢出错误。

假定使用以下签名为该函数配置了桩函数:

int foo(int param1, int param2);

  1. 启用对原始函数的调用:

    [C++] CPPTEST_ON_CALL("foo").Arg("__callOrig").Assign(1);
    [C] CPPTEST_ON_CALL("foo")->Arg("__callOrig")->Assign()->Int(1);
  2. 您还可以根据参数值启用对原始函数的调用。如果将在参数param1 设置为0的情况下调用桩函数,则将调用原始函数。在所有其他情况下,原始函数将不会被调用:

    [C++] CPPTEST_ON_CALL("foo").If().Arg("param1").Equal(0).Arg("__callOrig").Assign(1);
    [C] CPPTEST_ON_CALL("foo")->If()->Arg("param1")->Equal()->Int(0)->Arg("__callOrig")->Assign()→Int(1);

示例 2: 修改桩函数返回值

您可以修改桩函数返回的值。为此,C++test 生成的桩函数具有专用的名为 __return 的自变量。为该参数分配值可有效修改从桩函数返回的值。如果桩函数的返回值未修改,则返回默认值。

假定已使用以下签名为该函数配置了桩函数:

int foo(int param1, int param2);

  1. 您可以对桩函数的所有调用强制使用返回的整数值 5。

    [C++] CPPTEST_ON_CALL("foo").Arg("__return").Assign(5);
    [C] CPPTEST_ON_CALL("foo")->Arg("__return").Assign()->Int(5);


  2. 您可以根据桩函数参数值强制执行返回值。在以下示例中,对参数为param1等于整数0 的桩函数的调用强制返回整数值5。对于所有其他用例,将返回默认值(数字类型为 0)。

    [C++] CPPTEST_ON_CALL("foo").If().Arg("param1").Equal(0).Arg("__return").Assign(5);
    [C] CPPTEST_ON_CALL("foo")->If()->Arg("param1")->Equal()->Int(0)->Arg("__return")->Assign()->Int(5);
  3. 您可以调用原始函数并修改返回值。在以下示例中,将调用原始函数,并将返回值修改为 5 的整数。

    [C++] CPPTEST_ON_CALL("foo").Arg("__callOrig").Assign(1).Arg("__return").Assign(5);
    [C] CPPTEST_ON_CALL("foo")->Arg("__callOrig")->Assign()->Int(1)->Arg("__return")->Assign()->Int(5);

示例 3: 检查桩函数调用参数的值

您可以针对预定义值测试桩函数调用参数的值。C++test 生成的桩函数具有专用的命名参数,它们表示桩函数调用参数。桩函数行为定义参数的名称与原始函数声明中使用的名称相同。

假定已使用以下签名为该函数配置了桩函数:

int foo(int param1, int param2);

测试以确定桩函数调用参数param1是否等于0。 如果不是,则测试失败,并显示所提供的消息。

[C++] CPPTEST_ON_CALL("foo").If().Arg("param1").NotEqual(0).Fail("Incorrect param value");
[C] CPPTEST_ON_CALL("foo")->If()->Arg("param1")->NotEqual()->Int(0)->Fail("Incorrect param value");

示例 4: 修改桩函数调用参数的值

与前面的示例一样,您可以为桩函数调用参数分配值。通常,在请求调用原始函数/方法时使用此方案。

假定已使用以下签名为该函数配置了存根:

int foo(int param1, int param2);

  1. 您可以为桩函数调用参数分配一个值。在此示例中,将整数 1分配给桩函数调用参数param1

    [C++] CPPTEST_ON_CALL("foo").Arg("param1").Assign(1);
    [C] CPPTEST_ON_CALL("foo")->Arg("param1")->Assign()->Int(1);
  2. 您可以根据另一个参数为该参数分配一个值。在此示例中,如果 param1的值等于整数值0,则将整数值1分配给param2

    [C++] CPPTEST_ON_CALL("foo").If().Arg("param1").Equal(0).Arg("param2").Assign(1);
    [C] CPPTEST_ON_CALL("foo")->If()->Arg("param1")->Equal()->Int(0)->Arg("param2")->Assign()->Int(1);
  • No labels