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).
Macro | Definition |
---|---|
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 behaviorargs
: Sequence of arguments passed to stub behavior executionfunc-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>.
Theparent::
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 definition | Stub 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
against0
and report a problem message ifparasm1
is equal to0
. - 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 method | Description |
---|---|
Arg(const char *argName) | Argument is determined by its name provided as character string. The name used in Example: [ |
ArgByExpr() | Argument is determined by evaluating subsequent expression, which should have const char * type. Example:
It is assumed that {
|
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);
- 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);
- 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 to0
, 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);
- 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);
- 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 value0
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);
- 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 to0
. 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);
- 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);
- 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);