Issue
I'm trying to use the FFF mocking library for C (https://github.com/meekrosoft/fff) to unit test C code. The issue I'm running into is gcc is seeing my mock object as a redefinition of the original function and throwing compiler errors because of it.
I created 2 testing files that mimick my use cases. my_lib.c
has definitions of some internal functions. test.c
is importing my_lib.c
because the intent is to test all the functions from the source file.
//my_lib.c
int thing = 0;
int get_thing () {
return thing;
}
int do_thing (void) {
return thing;
}
int set_thing (int x) {
thing = x;
return do_thing();
}
//test.c
#include "fff.h"
#include "my_lib.c"
DEFINE_FFF_GLOBALS;
FAKE_VALUE_FUNC0(int, do_thing);
void setup(void)
{
// Reset the FFF call history between tests
FFF_RESET_HISTORY();
// Reset the FFF mock objects between tests
RESET_FAKE(do_thing);
}
void test_do_thing(void)
{
set_thing(11);
ASSERT_EQ(1, do_thing_fake.call_count);
}
void test_nested_mock(void)
{
do_thing_fake.return_val = -2;
int ret = set_thing(11); //set_thing() returns do_thing() which is mocked to return -2
ASSERT_EQ(-2, ret);
}
I'm compiling into an object file like so, but this gives me an immediate compilation error:
$ gcc -g -c test.c -o test.o
In file included from test.c:1:0:
test.c:7:23: error: redefinition of ‘do_thing’
FAKE_VALUE_FUNC0(int, do_thing);
^
fff.h:1632:45: note: in definition of macro ‘DEFINE_FAKE_VALUE_FUNC0’
RETURN_TYPE FFF_GCC_FUNCTION_ATTRIBUTES FUNCNAME(void){ \
^
test.c:7:1: note: in expansion of macro ‘FAKE_VALUE_FUNC0’
FAKE_VALUE_FUNC0(int, do_thing);
^
In file included from test.c:3:0:
my_lib.c:12:1: note: previous definition of ‘do_thing’ was here
do_thing (void)
^
I need to be able to mock out functions from source files while being able to test legacy source files without modifying any source or header files. What am I missing with fff and/or gcc?
EDIT: Let me add this. I could remove the definition of do_thing() from my_lib.c
and then my compilation, mocks, and tests work exactly as expected because all calls to do_thing now go to my mock object. I want to be able to mock a function that is already defined within the source code under test.
Solution
So the answer to your question depends on whether this function that you want to mock is a function you also want to test.
Option 1:
If you only want to mock the function in your tests and not use its original implementation, then the solution is simple:
add the weak
statement prior to the original function definition (in the c file). For example, with GCC you write: __attribute((weak))
before the function definition.
Before:
void foo(void)
After:
__attribute((weak)) void foo(void)
This tells the compiler that if there is another definition for the same function in the code, then the one with the weak
attribute is ignored.
In addition, if you don't want to change the source code you can add it in with #ifdef UNIT_TEST_ENABLED
and make sure that the definition UNIT_TEST_ENABLED is defined only the unit test project and not in the production code project.
For example:
#ifdef UNIT_TEST_ENABLED
__attribute((weak))
#endif
void foo(void)
Option 2:
If you want to use the original implementation in some of the tests but also be able to mock in other tests then I suggest you follow the instructions I wrote here: https://stackoverflow.com/a/65814339/4441211
Answered By - Eyal Gerber Answer Checked By - Robin (WPSolving Admin)