| # Fake Function Framework (fff) |
| ----------------------------- |
| |
| [![Build Status](https://travis-ci.org/meekrosoft/fff.svg?branch=master)](https://travis-ci.org/meekrosoft/fff) |
| |
| ## A Fake Function Framework for C |
| fff is a micro-framework for creating fake C functions for tests. Because life |
| is too short to spend time hand-writing fake functions for testing. |
| |
| |
| ## Hello fake world! |
| |
| Say you are testing an embedded user interface and you have a function that |
| you want to create a fake for: |
| |
| // UI.c |
| ... |
| void DISPLAY_init(); |
| ... |
| |
| Here's how you would define a fake function for this in your test suite: |
| |
| // test.c(pp) |
| #include "fff.h" |
| DEFINE_FFF_GLOBALS; |
| FAKE_VOID_FUNC(DISPLAY_init); |
| |
| And the unit test might look something like this: |
| |
| TEST_F(GreeterTests, init_initialises_display) |
| { |
| 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 |
| header only, all you need to do to use it is download <tt>fff.h</tt> and include |
| it in your test suite. |
| |
| The magic is in the <tt>FAKE_VOID_FUNC</tt>. This |
| expands a macro that defines a function returning <tt>void</tt> |
| which has zero arguments. It also defines a struct |
| <tt>"function_name"_fake</tt> which contains all the information about the fake. |
| For instance, <tt>DISPLAY_init_fake.call_count</tt>is incremented every time the faked |
| function is called. |
| |
| Under the hood it generates a struct that looks like this: |
| |
| typedef struct DISPLAY_init_Fake { |
| unsigned int call_count; |
| unsigned int arg_history_len; |
| unsigned int arg_histories_dropped; |
| void(*custom_fake)(); |
| } DISPLAY_init_Fake; |
| DISPLAY_init_Fake DISPLAY_init_fake; |
| |
| |
| |
| |
| |
| ## Capturing arguments |
| |
| Ok, enough with the toy examples. What about faking functions with arguments? |
| |
| // UI.c |
| ... |
| void DISPLAY_output(char * message); |
| ... |
| |
| Here's how you would define a fake function for this in your test suite: |
| |
| FAKE_VOID_FUNC(DISPLAY_output, char *); |
| |
| And the unit test might look something like this: |
| |
| TEST_F(UITests, write_line_outputs_lines_to_display) |
| { |
| char msg[] = "helloworld"; |
| UI_write_line(msg); |
| 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 |
| previous example. The number of arguments that the function takes is calculated, |
| and the macro arguments following the function name defines the argument |
| type (a char pointer in this example). |
| |
| A variable is created for every argument in the form |
| <tt>"function_name"fake.argN_val</tt> |
| |
| |
| |
| ## Return values |
| |
| When you want to define a fake function that returns a value, you should use the |
| <tt>FAKE_VALUE_FUNC</tt> macro. For instance: |
| |
| // UI.c |
| ... |
| 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: |
| |
| 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: |
| |
| TEST_F(UITests, when_empty_lines_write_line_doesnt_clear_screen) |
| { |
| // given |
| DISPLAY_get_line_insert_index_fake.return_val = 1; |
| char msg[] = "helloworld"; |
| // when |
| UI_write_line(msg); |
| // then |
| ASSERT_EQ(DISPLAY_clear_fake.call_count, 0); |
| } |
| |
| Of course you can mix and match these macros to define a value function with |
| arguments, for instance to fake: |
| |
| double pow(double base, double exponent); |
| |
| you would use a syntax like this: |
| |
| FAKE_VALUE_FUNC(double, pow, double, double); |
| |
| |
| |
| ## Resetting a fake |
| |
| Good tests are isolated tests, so it is important to reset the fakes for each |
| unit test. All the fakes have a reset function to reset their arguments and |
| call counts. It is good prectice is to call the reset function for all the |
| fakes in the setup function of your test suite. |
| |
| void setup() |
| { |
| // Register resets |
| RESET_FAKE(DISPLAY_init); |
| RESET_FAKE(DISPLAY_clear); |
| RESET_FAKE(DISPLAY_output_message); |
| RESET_FAKE(DISPLAY_get_line_capacity); |
| RESET_FAKE(DISPLAY_get_line_insert_index); |
| } |
| |
| You might want to define a macro to do this: |
| |
| ``` |
| /* List of fakes used by this unit tester */ |
| #define FFF_FAKES_LIST(FAKE) \ |
| FAKE(DISPLAY_init) \ |
| FAKE(DISPLAY_clear) \ |
| FAKE(DISPLAY_output_message) \ |
| FAKE(DISPLAY_get_line_capacity) \ |
| FAKE(DISPLAY_get_line_insert_index) |
| |
| void setup() |
| { |
| /* Register resets */ |
| FFF_FAKES_LIST(RESET_FAKE); |
| |
| /* reset common FFF internal structures */ |
| FFF_RESET_HISTORY(); |
| } |
| ``` |
| |
| ## Call history |
| Say you want to test that a function calls functionA, then functionB, then |
| functionA again, how would you do that? Well <tt>fff</tt> maintains a call |
| history so that it is easy to assert these expectations. |
| |
| Here's how it works: |
| |
| FAKE_VOID_FUNC(voidfunc2, char, char); |
| FAKE_VALUE_FUNC(long, longfunc0); |
| |
| TEST_F(FFFTestSuite, calls_in_correct_order) |
| { |
| longfunc0(); |
| voidfunc2(); |
| longfunc0(); |
| |
| ASSERT_EQ(fff.call_history[0], (void *)longfunc0); |
| ASSERT_EQ(fff.call_history[1], (void *)voidfunc2); |
| ASSERT_EQ(fff.call_history[2], (void *)longfunc0); |
| } |
| |
| They are reset by calling <tt>FFF_RESET_HISTORY();</tt> |
| |
| |
| ## Default Argument History |
| |
| The framework will by default store the arguments for the last ten calls made |
| to a fake function. |
| |
| TEST_F(FFFTestSuite, when_fake_func_called_then_arguments_captured_in_history) |
| { |
| voidfunc2('g', 'h'); |
| voidfunc2('i', 'j'); |
| ASSERT_EQ('g', voidfunc2_fake.arg0_history[0]); |
| ASSERT_EQ('h', voidfunc2_fake.arg1_history[0]); |
| 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 |
| check the dropped histories counter: |
| |
| TEST_F(FFFTestSuite, when_fake_func_called_max_times_plus_one_then_one_argument_history_dropped) |
| { |
| int i; |
| for(i = 0; i < 10; i++) |
| { |
| voidfunc2('1'+i, '2'+i); |
| } |
| 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: |
| |
| ASSERT(voidfunc2_fake.arg_history_len < voidfunc2_fake.call_count); |
| |
| The argument histories for a fake function are reset when the RESET_FAKE |
| function is called |
| |
| ## User Defined Argument History |
| |
| 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: |
| |
| // Want to keep the argument history for 13 calls |
| #define FFF_ARG_HISTORY_LEN 13 |
| // Want to keep the call sequence history for 17 function calls |
| #define FFF_CALL_HISTORY_LEN 17 |
| |
| #include "../fff.h" |
| |
| |
| ## Function Return Value Sequences |
| |
| Often in testing we would like to test the behaviour of sequence of function call |
| 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: |
| |
| // faking "long longfunc();" |
| FAKE_VALUE_FUNC(long, longfunc0); |
| |
| TEST_F(FFFTestSuite, return_value_sequences_exhausted) |
| { |
| long myReturnVals[3] = { 3, 7, 9 }; |
| SET_RETURN_SEQ(longfunc0, myReturnVals, 3); |
| ASSERT_EQ(myReturnVals[0], longfunc0()); |
| ASSERT_EQ(myReturnVals[1], 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, |
| the fake will return the values given in the parameter array in sequence. When |
| the end of the sequence is reached the fake will continue to return the last |
| value in the sequence indefinitely. |
| |
| ## Custom Return Value Delegate |
| |
| 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: |
| |
| #define MEANING_OF_LIFE 42 |
| long my_custom_value_fake(void) |
| { |
| return MEANING_OF_LIFE; |
| } |
| 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(); |
| ASSERT_EQ(MEANING_OF_LIFE, retval); |
| } |
| |
| ## Custom Return Value Delegate Sequences |
| |
| Say you have a function with an out parameter, and you want it to have a different behaviour |
| on the first three calls, for example: set the value 'x' to the out parameter on the first call, |
| the value 'y' to the out parameter on the second call, and the value 'z' to the out parameter |
| 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: |
| |
| void voidfunc1outparam_custom_fake1(char *a) |
| { |
| *a = 'x'; |
| } |
| |
| void voidfunc1outparam_custom_fake2(char *a) |
| { |
| *a = 'y'; |
| } |
| |
| void voidfunc1outparam_custom_fake3(char *a) |
| { |
| *a = 'z'; |
| } |
| |
| TEST_F(FFFTestSuite, custom_fake_sequence_not_exausthed) |
| { |
| void (*custom_fakes[])(char *) = {voidfunc1outparam_custom_fake1, |
| voidfunc1outparam_custom_fake2, |
| voidfunc1outparam_custom_fake3}; |
| char a = 'a'; |
| |
| SET_CUSTOM_FAKE_SEQ(voidfunc1outparam, custom_fakes, 3); |
| |
| voidfunc1outparam(&a); |
| ASSERT_EQ('x', a); |
| voidfunc1outparam(&a); |
| ASSERT_EQ('y', a); |
| voidfunc1outparam(&a); |
| ASSERT_EQ('z', a); |
| } |
| |
| 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 |
| fake in the sequence. This macro works much like the SET_RETURN_SEQ macro. |
| |
| ## How do I fake a function that returns a value by reference? |
| The basic mechanism that FFF provides you in this case is the custom_fake field described in the *Custom Return Value Delegate* example above. |
| |
| You need to create a custom function (e.g. getTime_custom_fake) to produce the output optionally by use of a helper variable (e.g. getTime_custom_now) to retrieve that output from. Then some creativity to tie it all together. The most important part (IMHO) is to keep your test case readable and maintainable. |
| |
| 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 */ |
| typedef struct { |
| int hour, min; |
| } Time; |
| |
| /* Our fake function */ |
| FAKE_VOID_FUNC(getTime, Time*); |
| |
| /* A test using the getTime fake function */ |
| TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_output) |
| { |
| Time t; |
| Time getTime_custom_now; |
| void getTime_custom_fake(Time *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); |
| } |
| |
| ## 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. |
| |
| If you need to stub a function that has a function pointer parameter, e.g. something like: |
| |
| ``` |
| /* timer.h */ |
| typedef int timer_handle; |
| extern int timer_start(timer_handle handle, long delay, void (*cb_function) (int arg), int arg); |
| ``` |
| |
| 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```. |
| |
| ``` |
| /* The fake, attempt one */ |
| FAKE_VALUE_FUNC(int, |
| timer_start, |
| timer_handle, |
| long, |
| void (*) (int argument), |
| 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. |
| |
| ``` |
| /* Additional type needed to be able to use callback in FFF */ |
| typedef void (*timer_cb) (int argument); |
| |
| /* The fake, attempt two */ |
| FAKE_VALUE_FUNC(int, |
| timer_start, |
| timer_handle, |
| long, |
| timer_cb, |
| int); |
| ``` |
| |
| Here are some ideas how to create a test case with callbacks. |
| |
| ``` |
| /* Unit test */ |
| TEST_F(FFFTestSuite, test_fake_with_function_pointer) |
| { |
| int cb_timeout_called = 0; |
| int result = 0; |
| |
| void cb_timeout(int argument) |
| { |
| cb_timeout_called++; |
| } |
| |
| int timer_start_custom_fake(timer_handle handle, |
| long delay, |
| void (*cb_function) (int arg), |
| int arg) |
| { |
| if (cb_function) cb_function(arg); |
| return timer_start_fake.return_val; |
| } |
| |
| /* given the custom fake for timer_start */ |
| timer_start_fake.return_val = 33; |
| timer_start_fake.custom_fake = timer_start_custom_fake; |
| |
| /* when timer_start is called |
| * (actually you would call your own function-under-test |
| * that would then call the fake function) |
| */ |
| result = timer_start(10, 100, cb_timeout, 55); |
| |
| /* then the timer_start fake must have been called correctly */ |
| ASSERT_EQ(result, 33); |
| ASSERT_EQ(timer_start_fake.call_count, 1); |
| ASSERT_EQ(timer_start_fake.arg0_val, 10); |
| ASSERT_EQ(timer_start_fake.arg1_val, 100); |
| ASSERT_EQ(timer_start_fake.arg2_val, cb_timeout); /* callback provided by unit tester */ |
| ASSERT_EQ(timer_start_fake.arg3_val, 55); |
| |
| /* and ofcourse our custom fake correctly calls the registered callback */ |
| ASSERT_EQ(cb_timeout_called, 1); |
| } |
| ``` |
| |
| ## Find out more... |
| |
| Look under the examlples directory for full length examples in both C and C++. |
| There is also a test suite for the framework under the test directory. |
| |
| ------------------------- |
| |
| ## Benefits |
| So whats the point? |
| |
| * To make it easy to create fake functions for testing C code. |
| * It is simple - just include a header file and you are good to go. |
| * To work in both C and C++ test environments |
| |
| |
| ## Under the hood: |
| * The fff.h header file is generated by a ruby script |
| * There are tests under src/test |
| * There is an example for testing an embedded UI and a hardware driver under src/examples |
| |
| |
| ## Cheat Sheet |
| <table> |
| <tr> |
| <th>Macro</th> |
| <th>Description</th> |
| <th>Example</th> |
| </tr> |
| <tr> |
| <td>FAKE_VOID_FUNC(fn [,arg_types*]);</td> |
| <td>Define a fake function named fn returning void with n arguments</td> |
| <td>FAKE_VOID_FUNC(DISPLAY_output_message, const char*);</td> |
| </tr> |
| <tr> |
| <td>FAKE_VALUE_FUNC(return_type, fn [,arg_types*]);</td> |
| <td>Define a fake function returning a value with type return_type taking n arguments</td> |
| <td>FAKE_VALUE_FUNC(int, DISPLAY_get_line_insert_index);</td> |
| </tr> |
| <tr> |
| <td>RESET_FAKE(fn);</td> |
| <td>Reset the state of fake function called fn</td> |
| <td>RESET_FAKE(DISPLAY_init);</td> |
| </tr> |
| </table> |