Search code examples
cunit-testingoopstubcunit

C Unit Test: stub a constant structure (gcc --wrap)


Hi All here is my specific case:

service.h:

    typedef struct {
        uint8_t (*function1)(void);
        uint8_t (*function2)(void);
    } const service_struct_t;

    extern service_struct_t service_api ;

service.c:

    #include "service.h"

    static uint8_t foo(void){
         return 13+6;
    }
    static uint8_t bar(void){
         return 7*6;
    }

    service_struct_t service_api = {
        .function1 = foo,
        .function2 = bar,
    };

I need to stub (to mock, to replace) these functions but I have no right to change that original code. I'm using gcc to compile the unit tests. I've failed to:

  • use the --wrap option of gcc straight on foo and bar since they are static to source.c :

    #include "service.h"
    #define ENABLE_STUB 1 /* that is actually a variable toggled at runtime */
    uint8_t __real_foo(void);
    uint8_t __wrap_foo(void){
        if(ENABLE_STUB){
            return 1;
        }else{
            return __real_foo();
        }
    }
    /* same for bar */

  • use the --wrap option of gcc onto the service_api object symbol because it's not a function

    #include "service.h"
    #define ENABLE_STUB 1 /* that is actually a variable toggled at runtime */
    uint8_t __real_service_api ;
    uint8_t __wrap_service_api = {
        .function1 = foo,
        .function2 = bar,
    }

    static uint8_t foo(void){
        if(ENABLE_STUB){
            return 1;
        }else{
            return __real_service_api.function1();
        }
    }
    /* same for bar */

  • simply reassign the service_api member functions since the structure is constant and already assigned.

    #include "service.h"
    #define ENABLE_STUB 1 /* that is actually a variable toggled at runtime */
    service_struct_t backup_service_api = {
        .function1 = service_api.function1;
        .function2 = service_api.function2;
    }
    service_struct_t stub_service_api = {
        .function1 = foo;
        .function2 = bar;
    }

    uint8_t foo(void){
        if(ENABLE_STUB){
            return 1;
        }else{
            return __real_foo();
        }
    }/* same for bar */

    void service_poke_stub(bool_t enable_stubs){
        if(enable_stubs){
            service_api.function1 = stub_service_api.function1
            service_api.function2 = stub_service_api.function2
        }else{
            service_api.function1 = backup_service_api.function1
            service_api.function2 = backup_service_api.function2
        }
    }

thanks already for your help


Solution

  • You can't mock the functions in the structure, as you already found out.

    So it depends on what you like to test:

    • If you want to test whether the structure contains the correct functions, the module service.c is your module-under-test and should be used as is. You need to check the correctness by watching what is done by the functions.

    • If you want to test that the structure is used correctly, you will mock the whole module. Now you are free to put in it whatever you want.

    If your source code does not allow this, the design is bad for testing. This is often the case when the architecture is not done with testability in mind.