Difference between revisions of "Using Test Doubles"

From STRIDE Wiki
Jump to: navigation, search
(Switching the Double Function During Runtime)
Line 1: Line 1:
==Introduction==
+
__NOTOC__
 
The ''Test Double'' feature provides a means for intercepting C/C++ language global functions on the target, and directing the call to a substitute function with identical parameters and return value. The use case is a unit test where the function under test uses a (global) function during its execution, and this dependency is simulated by a substitute or double function during testing. The unit test is able to control the substitution of the dependency during run time, and thereby verify the behavior of the function under test.
 
The ''Test Double'' feature provides a means for intercepting C/C++ language global functions on the target, and directing the call to a substitute function with identical parameters and return value. The use case is a unit test where the function under test uses a (global) function during its execution, and this dependency is simulated by a substitute or double function during testing. The unit test is able to control the substitution of the dependency during run time, and thereby verify the behavior of the function under test.
 
The following sample illustrates the relationship of the function under test and a dependency:
 
The following sample illustrates the relationship of the function under test and a dependency:
Line 8: Line 8:
 
</source>
 
</source>
 
   
 
   
 +
 
<source lang="c">
 
<source lang="c">
 
// depend.c
 
// depend.c
Line 17: Line 18:
 
}
 
}
 
</source>
 
</source>
 +
 
   
 
   
 
<source lang="c">
 
<source lang="c">
Line 23: Line 25:
 
</source>
 
</source>
 
   
 
   
 +
 
<source lang="c">
 
<source lang="c">
 
// test.c
 
// test.c
Line 33: Line 36:
 
}
 
}
 
</source>
 
</source>
 +
  
 
In the above sample, ''test()'' is the function under test and ''depend()'' is a dependency candidate for doubling.
 
In the above sample, ''test()'' is the function under test and ''depend()'' is a dependency candidate for doubling.
  
 +
The steps required to achieve doubling of a dependency function are as follows:
 +
#[[#Configuring_the_Double_Using_SCL|Configure the Double using SCL]]
 +
#[[#Creating_Double_Intercepts_in_the_IM|Create Double Intercepts in the IM]]
 +
#[[#Switching_the_Double_Function_During_Runtime|Switch Double Function During Runtime]]
  
The steps required to achieve doubling of a dependency function are as follows:
 
#[[#Configuring_the_Double_Using_SCL|Configure the double parameters using SCL pragmas]]
 
#[[#Creating_Double_Intercepts_in_the_IM|Create the double intercepts in the IM]]
 
#[[#Switching_the_Double_Function_During_Runtime|Switch to/from the double function during runtime]]
 
  
 
==Configuring the Double Using SCL==
 
==Configuring the Double Using SCL==
Line 54: Line 58:
 
#pragma scl_function(depend, "DEFINITION", "IMPLICIT", "TEST_GROUP")
 
#pragma scl_function(depend, "DEFINITION", "IMPLICIT", "TEST_GROUP")
 
</source>
 
</source>
 +
  
 
==Creating Double Intercepts in the IM==
 
==Creating Double Intercepts in the IM==
Line 72: Line 77:
 
}
 
}
 
</source>
 
</source>
 +
  
 
==Switching the Double Function During Runtime==
 
==Switching the Double Function During Runtime==

Revision as of 16:04, 1 July 2015

The Test Double feature provides a means for intercepting C/C++ language global functions on the target, and directing the call to a substitute function with identical parameters and return value. The use case is a unit test where the function under test uses a (global) function during its execution, and this dependency is simulated by a substitute or double function during testing. The unit test is able to control the substitution of the dependency during run time, and thereby verify the behavior of the function under test. The following sample illustrates the relationship of the function under test and a dependency:

// depend.h
int depend(int x);


// depend.c
#include "depend.h"
 
int depend(int x)
{
    return x + x;
}


// test.h
int test(int x, int y);


// test.c
#include "test.h"
#include "depend.h"
 
int test(int x, int y)
{
    return depend(x) * y;
}


In the above sample, test() is the function under test and depend() is a dependency candidate for doubling.

The steps required to achieve doubling of a dependency function are as follows:

  1. Configure the Double using SCL
  2. Create Double Intercepts in the IM
  3. Switch Double Function During Runtime


Configuring the Double Using SCL

The syntax of the scl_function pragma supports a set of optional attributes that allow the specification of function interception parameters.

In the above sample, depend() needs to be SCL captured and enabled for interception in a manner to be doubled.

// depend_scl.h
#include "depend.h"

#pragma scl_function(depend, "DEFINITION", "IMPLICIT", "TEST_GROUP")


Creating Double Intercepts in the IM

If a function has been configured as a double candidate using SCL as outlined in the above step, then the next step is, using STRIDE build tools, to create the Intercept Module, aka IM, that contains the intercept for the double function.

Once the IM has been successfully created, the source file containing the depend() implementation must be altered to mangle the function's name by defining the Group ID and including the generated Intercept Module header file (xxxIM.h):

// depend.c
#include "depend.h"
/* define the Group ID before including the IM header */
#define TEST_GROUP
#include "strideIM.h"

int depend(int x)
{
    return x + x;
}


Switching the Double Function During Runtime

In the context of the test unit code the following STRIDE runtime macros, defined in the STRIDE runtime header file srtest.h, could be used for substituting a stub function for a double candidate.

srDOUBLE_SET(fn, fnDbl)
srDOUBLE_RESET(fn)

srDOUBLE_GET(fn, pfnDbl) // used for more advanced scenarios

Where:

  • fn is the function qualified by scl_function or scl_func as a dependency candidate, as above.
  • pfnDbl is a pointer to a object of type srFunctionDouble_t, declared to hold the current value of the active double function.
  • fnDbl is a function that is to be the current active double. The function passed in should always match the signature of the dependency candidate specified by fn.

Note: the initial value of the current active double is always the dependency candidate function.

Example 1

The following example shows how to double a routine for the lifetime of a C++ test unit:

#include <srtest.h>
#include "depend.h"

extern "C" int depend_dbl(int a) { return a * a; }

class Test: public stride::srTest
{ 
public:

    Test()
    {
        srDOUBLE_SET(depend, depend_dbl);
    }

    ~Test()
    {
        srDOUBLE_RESET(depend);
    }

    int test1(void) { return test(1, 2); }
    int test2(void) { return test(5, 6); }
};
 
#ifdef _SCL
#pragma scl_test_class(Test)
#pragma scl_function(depend, "DEFINITION", "IMPLICIT", "TEST_GROUP")
#endif

Example 2

The following example shows how to make a call to the original routine in the context of a double by safely handling any nested doubling:

#include <srtest.h>
#include "depend.h"

extern "C" int depend_dbl(int a) 
{
    int ret;

    srFunctionDouble_t fn;
    srDOUBLE_GET(depend, &fn);
    srDOUBLE_RESET(depend);
    ret = depend(a);
    srDOUBLE_SET(depend, fn);
    
    return ret; 
}