forked from 3rd-party/fff
Add syntax highlighting to README
This commit is contained in:
402
README.md
402
README.md
@@ -13,29 +13,35 @@ is too short to spend time hand-writing fake functions for testing.
|
|||||||
Say you are testing an embedded user interface and you have a function that
|
Say you are testing an embedded user interface and you have a function that
|
||||||
you want to create a fake for:
|
you want to create a fake for:
|
||||||
|
|
||||||
// UI.c
|
```c
|
||||||
...
|
// UI.c
|
||||||
void DISPLAY_init();
|
...
|
||||||
...
|
void DISPLAY_init();
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Here's how you would define a fake function for this in your test suite:
|
Here's how you would define a fake function for this in your test suite:
|
||||||
|
|
||||||
// test.c(pp)
|
```c
|
||||||
#include "fff.h"
|
// test.c(pp)
|
||||||
DEFINE_FFF_GLOBALS;
|
#include "fff.h"
|
||||||
FAKE_VOID_FUNC(DISPLAY_init);
|
DEFINE_FFF_GLOBALS;
|
||||||
|
FAKE_VOID_FUNC(DISPLAY_init);
|
||||||
|
```
|
||||||
|
|
||||||
And the unit test might look something like this:
|
And the unit test might look something like this:
|
||||||
|
|
||||||
TEST_F(GreeterTests, init_initialises_display)
|
```c
|
||||||
{
|
TEST_F(GreeterTests, init_initialises_display)
|
||||||
UI_init();
|
{
|
||||||
ASSERT_EQ(DISPLAY_init_fake.call_count, 1);
|
UI_init();
|
||||||
}
|
ASSERT_EQ(DISPLAY_init_fake.call_count, 1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
So what has happened here? The first thing to note is that the framework is
|
So what has happened here? The first thing to note is that the framework is
|
||||||
header only, all you need to do to use it is download <tt>fff.h</tt> and include
|
header only, all you need to do to use it is download <tt>fff.h</tt> and include
|
||||||
it in your test suite.
|
it in your test suite.
|
||||||
|
|
||||||
The magic is in the <tt>FAKE_VOID_FUNC</tt>. This
|
The magic is in the <tt>FAKE_VOID_FUNC</tt>. This
|
||||||
expands a macro that defines a function returning <tt>void</tt>
|
expands a macro that defines a function returning <tt>void</tt>
|
||||||
@@ -46,13 +52,15 @@ function is called.
|
|||||||
|
|
||||||
Under the hood it generates a struct that looks like this:
|
Under the hood it generates a struct that looks like this:
|
||||||
|
|
||||||
typedef struct DISPLAY_init_Fake {
|
```c
|
||||||
unsigned int call_count;
|
typedef struct DISPLAY_init_Fake {
|
||||||
unsigned int arg_history_len;
|
unsigned int call_count;
|
||||||
unsigned int arg_histories_dropped;
|
unsigned int arg_history_len;
|
||||||
void(*custom_fake)();
|
unsigned int arg_histories_dropped;
|
||||||
} DISPLAY_init_Fake;
|
void(*custom_fake)();
|
||||||
DISPLAY_init_Fake DISPLAY_init_fake;
|
} DISPLAY_init_Fake;
|
||||||
|
DISPLAY_init_Fake DISPLAY_init_fake;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -62,24 +70,30 @@ Under the hood it generates a struct that looks like this:
|
|||||||
|
|
||||||
Ok, enough with the toy examples. What about faking functions with arguments?
|
Ok, enough with the toy examples. What about faking functions with arguments?
|
||||||
|
|
||||||
// UI.c
|
```c
|
||||||
...
|
// UI.c
|
||||||
void DISPLAY_output(char * message);
|
...
|
||||||
...
|
void DISPLAY_output(char * message);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Here's how you would define a fake function for this in your test suite:
|
Here's how you would define a fake function for this in your test suite:
|
||||||
|
|
||||||
FAKE_VOID_FUNC(DISPLAY_output, char *);
|
```c
|
||||||
|
FAKE_VOID_FUNC(DISPLAY_output, char *);
|
||||||
|
```
|
||||||
|
|
||||||
And the unit test might look something like this:
|
And the unit test might look something like this:
|
||||||
|
|
||||||
TEST_F(UITests, write_line_outputs_lines_to_display)
|
```c
|
||||||
{
|
TEST_F(UITests, write_line_outputs_lines_to_display)
|
||||||
char msg[] = "helloworld";
|
{
|
||||||
UI_write_line(msg);
|
char msg[] = "helloworld";
|
||||||
ASSERT_EQ(DISPLAY_output_fake.call_count, 1);
|
UI_write_line(msg);
|
||||||
ASSERT_EQ(strncmp(DISPLAY_output_fake.arg0_val, msg, 26), 0);
|
ASSERT_EQ(DISPLAY_output_fake.call_count, 1);
|
||||||
}
|
ASSERT_EQ(strncmp(DISPLAY_output_fake.arg0_val, msg, 26), 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
There is no more magic here, the <tt>FAKE_VOID_FUNC</tt> works as in the
|
There is no more magic here, the <tt>FAKE_VOID_FUNC</tt> works as in the
|
||||||
@@ -97,38 +111,48 @@ A variable is created for every argument in the form
|
|||||||
When you want to define a fake function that returns a value, you should use the
|
When you want to define a fake function that returns a value, you should use the
|
||||||
<tt>FAKE_VALUE_FUNC</tt> macro. For instance:
|
<tt>FAKE_VALUE_FUNC</tt> macro. For instance:
|
||||||
|
|
||||||
// UI.c
|
```c
|
||||||
...
|
// UI.c
|
||||||
unsigned int DISPLAY_get_line_capacity();
|
...
|
||||||
unsigned int DISPLAY_get_line_insert_index();
|
unsigned int DISPLAY_get_line_capacity();
|
||||||
...
|
unsigned int DISPLAY_get_line_insert_index();
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Here's how you would define fake functions for these in your test suite:
|
Here's how you would define fake functions for these in your test suite:
|
||||||
|
|
||||||
FAKE_VALUE_FUNC(unsigned int, DISPLAY_get_line_capacity);
|
```c
|
||||||
FAKE_VALUE_FUNC(unsigned int, DISPLAY_get_line_insert_index);
|
FAKE_VALUE_FUNC(unsigned int, DISPLAY_get_line_capacity);
|
||||||
|
FAKE_VALUE_FUNC(unsigned int, DISPLAY_get_line_insert_index);
|
||||||
|
```
|
||||||
|
|
||||||
And the unit test might look something like this:
|
And the unit test might look something like this:
|
||||||
|
|
||||||
TEST_F(UITests, when_empty_lines_write_line_doesnt_clear_screen)
|
```c
|
||||||
{
|
TEST_F(UITests, when_empty_lines_write_line_doesnt_clear_screen)
|
||||||
// given
|
{
|
||||||
DISPLAY_get_line_insert_index_fake.return_val = 1;
|
// given
|
||||||
char msg[] = "helloworld";
|
DISPLAY_get_line_insert_index_fake.return_val = 1;
|
||||||
// when
|
char msg[] = "helloworld";
|
||||||
UI_write_line(msg);
|
// when
|
||||||
// then
|
UI_write_line(msg);
|
||||||
ASSERT_EQ(DISPLAY_clear_fake.call_count, 0);
|
// then
|
||||||
}
|
ASSERT_EQ(DISPLAY_clear_fake.call_count, 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Of course you can mix and match these macros to define a value function with
|
Of course you can mix and match these macros to define a value function with
|
||||||
arguments, for instance to fake:
|
arguments, for instance to fake:
|
||||||
|
|
||||||
double pow(double base, double exponent);
|
```c
|
||||||
|
double pow(double base, double exponent);
|
||||||
|
```
|
||||||
|
|
||||||
you would use a syntax like this:
|
you would use a syntax like this:
|
||||||
|
|
||||||
FAKE_VALUE_FUNC(double, pow, double, double);
|
```c
|
||||||
|
FAKE_VALUE_FUNC(double, pow, double, double);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -139,19 +163,21 @@ unit test. All the fakes have a reset function to reset their arguments and
|
|||||||
call counts. It is good practice is to call the reset function for all the
|
call counts. It is good practice is to call the reset function for all the
|
||||||
fakes in the setup function of your test suite.
|
fakes in the setup function of your test suite.
|
||||||
|
|
||||||
void setup()
|
```c
|
||||||
{
|
void setup()
|
||||||
// Register resets
|
{
|
||||||
RESET_FAKE(DISPLAY_init);
|
// Register resets
|
||||||
RESET_FAKE(DISPLAY_clear);
|
RESET_FAKE(DISPLAY_init);
|
||||||
RESET_FAKE(DISPLAY_output_message);
|
RESET_FAKE(DISPLAY_clear);
|
||||||
RESET_FAKE(DISPLAY_get_line_capacity);
|
RESET_FAKE(DISPLAY_output_message);
|
||||||
RESET_FAKE(DISPLAY_get_line_insert_index);
|
RESET_FAKE(DISPLAY_get_line_capacity);
|
||||||
}
|
RESET_FAKE(DISPLAY_get_line_insert_index);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
You might want to define a macro to do this:
|
You might want to define a macro to do this:
|
||||||
|
|
||||||
```
|
```c
|
||||||
/* List of fakes used by this unit tester */
|
/* List of fakes used by this unit tester */
|
||||||
#define FFF_FAKES_LIST(FAKE) \
|
#define FFF_FAKES_LIST(FAKE) \
|
||||||
FAKE(DISPLAY_init) \
|
FAKE(DISPLAY_init) \
|
||||||
@@ -177,19 +203,21 @@ history so that it is easy to assert these expectations.
|
|||||||
|
|
||||||
Here's how it works:
|
Here's how it works:
|
||||||
|
|
||||||
FAKE_VOID_FUNC(voidfunc2, char, char);
|
```c
|
||||||
FAKE_VALUE_FUNC(long, longfunc0);
|
FAKE_VOID_FUNC(voidfunc2, char, char);
|
||||||
|
FAKE_VALUE_FUNC(long, longfunc0);
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, calls_in_correct_order)
|
TEST_F(FFFTestSuite, calls_in_correct_order)
|
||||||
{
|
{
|
||||||
longfunc0();
|
longfunc0();
|
||||||
voidfunc2();
|
voidfunc2();
|
||||||
longfunc0();
|
longfunc0();
|
||||||
|
|
||||||
ASSERT_EQ(fff.call_history[0], (void *)longfunc0);
|
ASSERT_EQ(fff.call_history[0], (void *)longfunc0);
|
||||||
ASSERT_EQ(fff.call_history[1], (void *)voidfunc2);
|
ASSERT_EQ(fff.call_history[1], (void *)voidfunc2);
|
||||||
ASSERT_EQ(fff.call_history[2], (void *)longfunc0);
|
ASSERT_EQ(fff.call_history[2], (void *)longfunc0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
They are reset by calling <tt>FFF_RESET_HISTORY();</tt>
|
They are reset by calling <tt>FFF_RESET_HISTORY();</tt>
|
||||||
|
|
||||||
@@ -199,33 +227,39 @@ They are reset by calling <tt>FFF_RESET_HISTORY();</tt>
|
|||||||
The framework will by default store the arguments for the last ten calls made
|
The framework will by default store the arguments for the last ten calls made
|
||||||
to a fake function.
|
to a fake function.
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, when_fake_func_called_then_arguments_captured_in_history)
|
```c
|
||||||
{
|
TEST_F(FFFTestSuite, when_fake_func_called_then_arguments_captured_in_history)
|
||||||
voidfunc2('g', 'h');
|
{
|
||||||
voidfunc2('i', 'j');
|
voidfunc2('g', 'h');
|
||||||
ASSERT_EQ('g', voidfunc2_fake.arg0_history[0]);
|
voidfunc2('i', 'j');
|
||||||
ASSERT_EQ('h', voidfunc2_fake.arg1_history[0]);
|
ASSERT_EQ('g', voidfunc2_fake.arg0_history[0]);
|
||||||
ASSERT_EQ('i', voidfunc2_fake.arg0_history[1]);
|
ASSERT_EQ('h', voidfunc2_fake.arg1_history[0]);
|
||||||
ASSERT_EQ('j', voidfunc2_fake.arg1_history[1]);
|
ASSERT_EQ('i', voidfunc2_fake.arg0_history[1]);
|
||||||
}
|
ASSERT_EQ('j', voidfunc2_fake.arg1_history[1]);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
There are two ways to find out if calls have been dropped. The first is to
|
There are two ways to find out if calls have been dropped. The first is to
|
||||||
check the dropped histories counter:
|
check the dropped histories counter:
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, when_fake_func_called_max_times_plus_one_then_one_argument_history_dropped)
|
```c
|
||||||
|
TEST_F(FFFTestSuite, when_fake_func_called_max_times_plus_one_then_one_argument_history_dropped)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
int i;
|
voidfunc2('1'+i, '2'+i);
|
||||||
for(i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
voidfunc2('1'+i, '2'+i);
|
|
||||||
}
|
|
||||||
voidfunc2('1', '2');
|
|
||||||
ASSERT_EQ(1u, voidfunc2_fake.arg_histories_dropped);
|
|
||||||
}
|
}
|
||||||
|
voidfunc2('1', '2');
|
||||||
|
ASSERT_EQ(1u, voidfunc2_fake.arg_histories_dropped);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The other is to check if the call count is greater than the history size:
|
The other is to check if the call count is greater than the history size:
|
||||||
|
|
||||||
ASSERT(voidfunc2_fake.arg_history_len < voidfunc2_fake.call_count);
|
```c
|
||||||
|
ASSERT(voidfunc2_fake.arg_history_len < voidfunc2_fake.call_count);
|
||||||
|
```
|
||||||
|
|
||||||
The argument histories for a fake function are reset when the RESET_FAKE
|
The argument histories for a fake function are reset when the RESET_FAKE
|
||||||
function is called
|
function is called
|
||||||
@@ -235,12 +269,14 @@ function is called
|
|||||||
If you wish to control how many calls to capture for argument history you can
|
If you wish to control how many calls to capture for argument history you can
|
||||||
override the default by defining it before include the <tt>fff.h</tt> like this:
|
override the default by defining it before include the <tt>fff.h</tt> like this:
|
||||||
|
|
||||||
// Want to keep the argument history for 13 calls
|
```c
|
||||||
#define FFF_ARG_HISTORY_LEN 13
|
// Want to keep the argument history for 13 calls
|
||||||
// Want to keep the call sequence history for 17 function calls
|
#define FFF_ARG_HISTORY_LEN 13
|
||||||
#define FFF_CALL_HISTORY_LEN 17
|
// Want to keep the call sequence history for 17 function calls
|
||||||
|
#define FFF_CALL_HISTORY_LEN 17
|
||||||
|
|
||||||
#include "../fff.h"
|
#include "../fff.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Function Return Value Sequences
|
## Function Return Value Sequences
|
||||||
@@ -249,19 +285,21 @@ Often in testing we would like to test the behaviour of sequence of function cal
|
|||||||
events. One way to do this with fff is to specify a sequence of return values
|
events. One way to do this with fff is to specify a sequence of return values
|
||||||
with for the fake function. It is probably easier to describe with an example:
|
with for the fake function. It is probably easier to describe with an example:
|
||||||
|
|
||||||
// faking "long longfunc();"
|
```c
|
||||||
FAKE_VALUE_FUNC(long, longfunc0);
|
// faking "long longfunc();"
|
||||||
|
FAKE_VALUE_FUNC(long, longfunc0);
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, return_value_sequences_exhausted)
|
TEST_F(FFFTestSuite, return_value_sequences_exhausted)
|
||||||
{
|
{
|
||||||
long myReturnVals[3] = { 3, 7, 9 };
|
long myReturnVals[3] = { 3, 7, 9 };
|
||||||
SET_RETURN_SEQ(longfunc0, myReturnVals, 3);
|
SET_RETURN_SEQ(longfunc0, myReturnVals, 3);
|
||||||
ASSERT_EQ(myReturnVals[0], longfunc0());
|
ASSERT_EQ(myReturnVals[0], longfunc0());
|
||||||
ASSERT_EQ(myReturnVals[1], longfunc0());
|
ASSERT_EQ(myReturnVals[1], longfunc0());
|
||||||
ASSERT_EQ(myReturnVals[2], longfunc0());
|
ASSERT_EQ(myReturnVals[2], longfunc0());
|
||||||
ASSERT_EQ(myReturnVals[2], longfunc0());
|
ASSERT_EQ(myReturnVals[2], longfunc0());
|
||||||
ASSERT_EQ(myReturnVals[2], longfunc0());
|
ASSERT_EQ(myReturnVals[2], longfunc0());
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
By specifying a return value sequence using the <tt>SET_RETURN_SEQ</tt> macro,
|
By specifying a return value sequence using the <tt>SET_RETURN_SEQ</tt> macro,
|
||||||
the fake will return the values given in the parameter array in sequence. When
|
the fake will return the values given in the parameter array in sequence. When
|
||||||
@@ -273,17 +311,19 @@ value in the sequence indefinitely.
|
|||||||
You can specify your own function to provide the return value for the fake. This
|
You can specify your own function to provide the return value for the fake. This
|
||||||
is done by setting the custom_fake member of the fake. Here's an example:
|
is done by setting the custom_fake member of the fake. Here's an example:
|
||||||
|
|
||||||
#define MEANING_OF_LIFE 42
|
```c
|
||||||
long my_custom_value_fake(void)
|
#define MEANING_OF_LIFE 42
|
||||||
{
|
long my_custom_value_fake(void)
|
||||||
return MEANING_OF_LIFE;
|
{
|
||||||
}
|
return MEANING_OF_LIFE;
|
||||||
TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_return_value)
|
}
|
||||||
{
|
TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_return_value)
|
||||||
longfunc0_fake.custom_fake = my_custom_value_fake;
|
{
|
||||||
long retval = longfunc0();
|
longfunc0_fake.custom_fake = my_custom_value_fake;
|
||||||
ASSERT_EQ(MEANING_OF_LIFE, retval);
|
long retval = longfunc0();
|
||||||
}
|
ASSERT_EQ(MEANING_OF_LIFE, retval);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Custom Return Value Delegate Sequences
|
## Custom Return Value Delegate Sequences
|
||||||
|
|
||||||
@@ -293,37 +333,39 @@ the value 'y' to the out parameter on the second call, and the value 'z' to the
|
|||||||
on the third call. You can specify a sequence of custom functions to a non-variadic function
|
on the third call. You can specify a sequence of custom functions to a non-variadic function
|
||||||
using the SET_CUSTOM_FAKE_SEQ macro. Here's an example:
|
using the SET_CUSTOM_FAKE_SEQ macro. Here's an example:
|
||||||
|
|
||||||
void voidfunc1outparam_custom_fake1(char *a)
|
```c
|
||||||
{
|
void voidfunc1outparam_custom_fake1(char *a)
|
||||||
*a = 'x';
|
{
|
||||||
}
|
*a = 'x';
|
||||||
|
}
|
||||||
|
|
||||||
void voidfunc1outparam_custom_fake2(char *a)
|
void voidfunc1outparam_custom_fake2(char *a)
|
||||||
{
|
{
|
||||||
*a = 'y';
|
*a = 'y';
|
||||||
}
|
}
|
||||||
|
|
||||||
void voidfunc1outparam_custom_fake3(char *a)
|
void voidfunc1outparam_custom_fake3(char *a)
|
||||||
{
|
{
|
||||||
*a = 'z';
|
*a = 'z';
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, custom_fake_sequence_not_exausthed)
|
TEST_F(FFFTestSuite, custom_fake_sequence_not_exausthed)
|
||||||
{
|
{
|
||||||
void (*custom_fakes[])(char *) = {voidfunc1outparam_custom_fake1,
|
void (*custom_fakes[])(char *) = {voidfunc1outparam_custom_fake1,
|
||||||
voidfunc1outparam_custom_fake2,
|
voidfunc1outparam_custom_fake2,
|
||||||
voidfunc1outparam_custom_fake3};
|
voidfunc1outparam_custom_fake3};
|
||||||
char a = 'a';
|
char a = 'a';
|
||||||
|
|
||||||
SET_CUSTOM_FAKE_SEQ(voidfunc1outparam, custom_fakes, 3);
|
SET_CUSTOM_FAKE_SEQ(voidfunc1outparam, custom_fakes, 3);
|
||||||
|
|
||||||
voidfunc1outparam(&a);
|
voidfunc1outparam(&a);
|
||||||
ASSERT_EQ('x', a);
|
ASSERT_EQ('x', a);
|
||||||
voidfunc1outparam(&a);
|
voidfunc1outparam(&a);
|
||||||
ASSERT_EQ('y', a);
|
ASSERT_EQ('y', a);
|
||||||
voidfunc1outparam(&a);
|
voidfunc1outparam(&a);
|
||||||
ASSERT_EQ('z', a);
|
ASSERT_EQ('z', a);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The fake will call your custom functions in the order specified by the SET_CUSTOM_FAKE_SEQ
|
The fake will call your custom functions in the order specified by the SET_CUSTOM_FAKE_SEQ
|
||||||
macro. When the last custom fake is reached the fake will keep calling the last custom
|
macro. When the last custom fake is reached the fake will keep calling the last custom
|
||||||
@@ -336,42 +378,44 @@ You need to create a custom function (e.g. getTime_custom_fake) to produce the o
|
|||||||
|
|
||||||
In case your project uses a C99 compliant C compiler you can even combine all this in a single unit test function so you can easily oversee all details of the test. See the example below.
|
In case your project uses a C99 compliant C compiler you can even combine all this in a single unit test function so you can easily oversee all details of the test. See the example below.
|
||||||
|
|
||||||
/* The time structure */
|
```c
|
||||||
typedef struct {
|
/* The time structure */
|
||||||
int hour, min;
|
typedef struct {
|
||||||
} Time;
|
int hour, min;
|
||||||
|
} Time;
|
||||||
|
|
||||||
/* Our fake function */
|
/* Our fake function */
|
||||||
FAKE_VOID_FUNC(getTime, Time*);
|
FAKE_VOID_FUNC(getTime, Time*);
|
||||||
|
|
||||||
/* A test using the getTime fake function */
|
/* A test using the getTime fake function */
|
||||||
TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_output)
|
TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_output)
|
||||||
{
|
{
|
||||||
Time t;
|
Time t;
|
||||||
Time getTime_custom_now;
|
Time getTime_custom_now;
|
||||||
void getTime_custom_fake(Time *now) {
|
void getTime_custom_fake(Time *now) {
|
||||||
*now = getTime_custom_now;
|
*now = getTime_custom_now;
|
||||||
}
|
|
||||||
getTime_fake.custom_fake = getTime_custom_fake;
|
|
||||||
|
|
||||||
/* given a specific time */
|
|
||||||
getTime_custom_now.hour = 13;
|
|
||||||
getTime_custom_now.min = 05;
|
|
||||||
|
|
||||||
/* when getTime is called */
|
|
||||||
getTime(&t);
|
|
||||||
|
|
||||||
/* then the specific time must be produced */
|
|
||||||
ASSERT_EQ(t.hour, 13);
|
|
||||||
ASSERT_EQ(t.min, 05);
|
|
||||||
}
|
}
|
||||||
|
getTime_fake.custom_fake = getTime_custom_fake;
|
||||||
|
|
||||||
|
/* given a specific time */
|
||||||
|
getTime_custom_now.hour = 13;
|
||||||
|
getTime_custom_now.min = 05;
|
||||||
|
|
||||||
|
/* when getTime is called */
|
||||||
|
getTime(&t);
|
||||||
|
|
||||||
|
/* then the specific time must be produced */
|
||||||
|
ASSERT_EQ(t.hour, 13);
|
||||||
|
ASSERT_EQ(t.min, 05);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## How do I fake a function with a function pointer parameter?
|
## How do I fake a function with a function pointer parameter?
|
||||||
Using FFF to stub functions that have function pointer parameter can cause problems when trying to stub them. Presented here is an example how to deal with this situation.
|
Using FFF to stub functions that have function pointer parameter can cause problems when trying to stub them. Presented here is an example how to deal with this situation.
|
||||||
|
|
||||||
If you need to stub a function that has a function pointer parameter, e.g. something like:
|
If you need to stub a function that has a function pointer parameter, e.g. something like:
|
||||||
|
|
||||||
```
|
```c
|
||||||
/* timer.h */
|
/* timer.h */
|
||||||
typedef int timer_handle;
|
typedef int timer_handle;
|
||||||
extern int timer_start(timer_handle handle, long delay, void (*cb_function) (int arg), int arg);
|
extern int timer_start(timer_handle handle, long delay, void (*cb_function) (int arg), int arg);
|
||||||
@@ -379,7 +423,7 @@ extern int timer_start(timer_handle handle, long delay, void (*cb_function) (int
|
|||||||
|
|
||||||
Then creating a fake like below will horribly fail when trying to compile because the FFF macro will internally expand into an illegal variable ```int (*)(int) arg2_val```.
|
Then creating a fake like below will horribly fail when trying to compile because the FFF macro will internally expand into an illegal variable ```int (*)(int) arg2_val```.
|
||||||
|
|
||||||
```
|
```c
|
||||||
/* The fake, attempt one */
|
/* The fake, attempt one */
|
||||||
FAKE_VALUE_FUNC(int,
|
FAKE_VALUE_FUNC(int,
|
||||||
timer_start,
|
timer_start,
|
||||||
@@ -391,7 +435,7 @@ FAKE_VALUE_FUNC(int,
|
|||||||
|
|
||||||
The solution to this problem is to create a bridging type that needs only to be visible in the unit tester. The fake will use that intermediate type. This way the compiler will not complain because the types match.
|
The solution to this problem is to create a bridging type that needs only to be visible in the unit tester. The fake will use that intermediate type. This way the compiler will not complain because the types match.
|
||||||
|
|
||||||
```
|
```c
|
||||||
/* Additional type needed to be able to use callback in FFF */
|
/* Additional type needed to be able to use callback in FFF */
|
||||||
typedef void (*timer_cb) (int argument);
|
typedef void (*timer_cb) (int argument);
|
||||||
|
|
||||||
@@ -406,7 +450,7 @@ FAKE_VALUE_FUNC(int,
|
|||||||
|
|
||||||
Here are some ideas how to create a test case with callbacks.
|
Here are some ideas how to create a test case with callbacks.
|
||||||
|
|
||||||
```
|
```c
|
||||||
/* Unit test */
|
/* Unit test */
|
||||||
TEST_F(FFFTestSuite, test_fake_with_function_pointer)
|
TEST_F(FFFTestSuite, test_fake_with_function_pointer)
|
||||||
{
|
{
|
||||||
@@ -415,7 +459,7 @@ TEST_F(FFFTestSuite, test_fake_with_function_pointer)
|
|||||||
|
|
||||||
void cb_timeout(int argument)
|
void cb_timeout(int argument)
|
||||||
{
|
{
|
||||||
cb_timeout_called++;
|
cb_timeout_called++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int timer_start_custom_fake(timer_handle handle,
|
int timer_start_custom_fake(timer_handle handle,
|
||||||
@@ -450,13 +494,13 @@ TEST_F(FFFTestSuite, test_fake_with_function_pointer)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
## How do I reuse a fake across multiple test-suites?
|
## How do I reuse a fake across multiple test-suites?
|
||||||
FFF functions like FAKE_VALUE_FUNC will perform both the declaration AND the definition of the fake function and the corresponding data structs. This cannot be placed in a header, since it will lead to multiple definitions of the fake functions.
|
FFF functions like FAKE_VALUE_FUNC will perform both the declaration AND the definition of the fake function and the corresponding data structs. This cannot be placed in a header, since it will lead to multiple definitions of the fake functions.
|
||||||
|
|
||||||
The solution is to separate declaration and definition of the fakes, and place the declaration into a public header file, and the definition into a private source file.
|
The solution is to separate declaration and definition of the fakes, and place the declaration into a public header file, and the definition into a private source file.
|
||||||
|
|
||||||
Here is an example of how it could be done:
|
Here is an example of how it could be done:
|
||||||
|
|
||||||
```
|
```c
|
||||||
/* Public header file */
|
/* Public header file */
|
||||||
#include "fff.h"
|
#include "fff.h"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user