Stub behavior can be dynamically configured to specific requirements for a 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 definition of the 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 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 identifi-ers according to the following patter: [parent::]<function_name>. The parent:: prefix is added only if the function/method is a class or namespace member. Only direct the parent is added. In the case of template class methods, template parameters are omitted in 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:

  • Test the value of param1 against 0 and report a problem message if parasm1 is equal to 0.
  • Assign 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++. C++ version of 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 allow 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)

Argument is determined by its name provided as character string. The name used in CPPTEST_ACTUAL_CALL and CPPTEST_ACTUAL_AFTER_CALL have to exactly match.

Example:

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

ArgByExpr()

Argument is determined by evaluating subsequent expression, which should have 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:

  • Parameters of the stub (reflecting parameters of stubbed function/method) are available by the names as specified in original function/method declaration
  • Variable used for returning value from the stub is available under the name __return
  • Variable used as a condition to redirect the call to original symbol definition is available by the name __callOrig

Specifying Values for Stub Behavior Definition

Values for the operations in the stub behavior definition can be provided “by value” or “by reference”. Functionally, this is the same concept as in C/C++. When an lvalue behavior is expected, the value that is passed 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);

  1. 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