Stub API require the Enable Stub API option to be enabled in the Stubs view (see Configuring Stub Options in the Stubs View) or directly in your test configuration (see Execution Tab Settings - Defining How Tests are Executed) before the stub is created.

You can dynamically configure stub behavior for specific requirements associated with the test case. Stub configuration is defined in source code using the dedicated API. You can specify a stub configuration at test suite setup/teardown, test case setup/teardown, and inside the test case. A stub configuration created inside a test case is removed immediately after test case execution. Similarly, with a stub configuration specified inside test case and test suite setup() functions, it is removed just after executing respective teardown() functions.

User-defined stub behavior is internally represented by “trigger objects.” When you define a trigger object, it accumulates your specified set of operations, values, and arguments references. Triggers perform defined operations when they are executed. The execution of trigger objects is supplemented with real values for the arguments referenced by name at the time of trigger object definition. The following table presents macros used to specify and execute stub behavior definition (trigger objects). 

MacroDefinition
CPPTEST_ON_CALL(func-id)<definition>Defines stub behavior (creates trigger object). Typically specified in the test case body.
CPPTEST_AFTER_CALL(func-id)<definition>Defines additional (optional) stub behavior executed only after the original function/method call was made from the stub (creates trigger objects). Typically specified in the test case body.
CPPTEST_ACTUAL_CALL(func-id)<args>Executes defined behavior for the stub. Always located inside the stub.
CPPTEST_ACTUAL_AFTER_CALL(func-id)<args>Executes defined behavior for the original function/method call post-processing. Always located inside the stub.
  • definition: Sequence of calls to the Stubs API that define expected stub behavior
  • args: Sequence of arguments passed to stub behavior execution
  • func-id: String used to identify the stub. By default, stubs generated by C++test use identifiers according to the following pattern: <parent::><function_name>. The parent:: prefix is added only if the function/method is a class or namespace member. Only the direct parent is added. In the case of template class methods, template parameters are omitted in the parent name.

C++test uses the same stub function-identifier for all overloaded versions of the function. If needed, you may introduce unique stub function-identifiers for each overloaded stub function by modifying stub definitions. This will allow you to distinguish stubs for overloaded functions/methods.

The following table shows a highly simplified example of the stub body used with the test case (C++ API) to visualize data and control flow.

Test case definitionStub definition
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; 
}

In this example, the stub for function foo() performs the following behaviors:

  • Tests the value of param1 against 0 and reports a problem message if parasm1 is equal to 0.
  • Assigns an integer value of  3 to the __return trigger argument, which represents a variable returned from the stub.

Stub behavior definition can include assignments, logical operations, relational operations, arithmetic operations, and executing callback functions. See Stubs API Reference, for a detailed description of the stub behavior definition grammar. There are some differences in the stubs API for C and C++ languages. All differences are highlighted in the grammar description and in provided examples. The following differences are particularly important:

  • Operators used to invoke chains of methods/functions when defining stub behavior are different for C and C++. The C++ version of the API uses the dot operator (.), while the C version uses the arrow operator (→)
  • When specifying a value in stub behavior definition, the C version requires the use of type-dedicated methods for passing values, while the C++ version allows for the use of a single overloaded method.

The stub behavior definition from the example above would have the following form in C language:

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

Note the use of the arrow operator (->) in place of dot operator (.), as well as the use of the Int() function call to pass an integer value.

Specifying Arguments for Stub Behavior Definition

You can specify arguments in the stub behavior definition by name or by the expression that evaluates to the argument name. Real values for the named arguments are provided during the execution of a defined behavior inside the stub as shown in the previous example on param1 and __return.

The following table shows API methods for specifying arguments to stub behavior definition:

API methodDescription
Arg(const char *argName)

The argument name provided as character string. The name used in CPPTEST_ACTUAL_CALL and CPPTEST_ACTUAL_AFTER_CALL must be exact matches.

Example:

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

ArgByExpr()

The argument is determined by evaluating the subsequent expression, which should have a const char * type.

Example:

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

It is assumed that {Arg(“arg_name”)} will evaluate during stub execution to “char *” type value, which will be used as an argument name, so:

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

Stubs generated by C++test use the following naming convention for default arguments:

  • Stub parameters (reflecting parameters of stubbed function/method) are available by name as specified in the original function/method declaration
  • Variables used for returning values from the stub are available under the name __return
  • Variables used as conditions to redirect calls to original symbol definitions are available by the name __callOrig

Specifying Values for Stub Behavior Definition

Values for the operations can be provided “by value” or “by reference,” which is functionally the same concept as in C/C++. When an lvalue behavior is expected, the passed value must be wrapped with a call to the ByRef() function. It is not necessary to wrap a value with ByRef() in situations where an rvalue semantic is expected.

It is typically preferable to pass variables by reference if an assignment is expected or if the evaluation of real value should be deferred until the time stub is executed. See example below:

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

In the first example, the value assigned to param1 will be copied when the CPPTEST_ON_CALL definition is processed. Any further changes to the real value of the global variable will not be reflected when performing an assignment.

In the second example, the value assigned to param1 will not be copied when processing the CPPTEST_ON_CALL definition. Instead, the “reference” to it will be stored (as a pointer), and the actual value of global will be taken when performing the assignment.

There is an important difference when specifying the values for stub behavior definition in C and in C++ mode. In C mode, the value specification must always be wrapped with a type-specific function. For example:

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

Wrapping functions are provided for all simple types. For a full list, refer to Stubs API Reference. In C++ mode, values can be provided without wrapping functions. In all the situations where value specification is required, you can use a generic “Value” function, which is overloaded for all simple types. For simpler notation, overloaded versions of “operator-methods” accept the value directly. This is shown in the examples below:

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);

Simpler notation using overloaded “operator” methods:

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);

The following section discusses typical use cases or the Stubs API.

Example 1: Calling Original Function

You can call an original function/method definition from the stub definition (assuming the original definition is available in the test binary). Calls to the original symbol is disabled by default. To enable this functionality, assign a non-zero value to the named argument __callOrig. Assign zero to this argument to disable.

If calling the original function is enabled but the original definition is not present, the stub will be called recursively without a stop-condition. This will cause stack overflow error.

Assume that a stub is configured for the function with the following signature:

int foo(int param1, int param2);

  1. Enable the call to original function:

    [C++] CPPTEST_ON_CALL("foo").Arg("__callOrig").Assign(1);
    [C] CPPTEST_ON_CALL("foo")->Arg("__callOrig")->Assign()->Int(1);
  2. You can also enable calls to the original function depending on the parameter value. If a stub will be called with parameter param1 set equal to 0, then the original function will be called. In all other cases the original function will not be called:

    [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);

Example 2: Modifying Stub Return Value

You can modify the value returned from the stub. Stubs generated by C++test have a dedicated named argument called __return for this purpose. Assigning a value to this argument effectively modifies the value returned from the stub. If the stub’s return value is not modified, then the default value is returned.

Assume that the stub is configured for the function with the following signature:

int foo(int param1, int param2);

  1. You can enforce the returning integer value 5 for all calls to the stub.

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


  2. You can enforce the return value depending on the stub parameter value. In the following example, the returning integer value 5 for the calls to the stub where parameter param1 is equal to the integer value 0 is enforced. For all other cases, the default value is returned (0 for numerical types).

    [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. You can call the original function and modify return values. In the following example, the original function is called and the return value is modified to the integer value of 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);

Example 3: Checking Stub Call Parameters’ Values

You can test the value of stub call parameters against predefined values. Stubs generated by C++test have dedicated named arguments that represent stub call parameters. The names of stub behavior definition arguments are the same as the names used in the original function declaration.

Assume that the stub is configured for the function with the following signature:

int foo(int param1, int param2);

Test to determine if the stub call parameter param1 is equal to 0. If not, the test fails with the provided message.

[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");

Example 4: Modifying Stub Call Paramters’ Values

As in the previous example, you can assign values for the stub call parameters. Typically, this scenario is used when a call to the original function/method is requested.

Assume that the stub is configured for the function with the following signature:

int foo(int param1, int param2);

  1. You can assign a value to the stub call parameter. In this example, the integer value 1 is assigned to the stub call parameter param1

    [C++] CPPTEST_ON_CALL("foo").Arg("param1").Assign(1);
    [C] CPPTEST_ON_CALL("foo")->Arg("param1")->Assign()->Int(1);
  2. You can assign a value to the parameter depending on another parameter. In this example, the integer value 1 is assigned to param2 if the value of param1 is equal integer value 0.

    [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