1

I am using FFF - Fake Function Framework to stub out some calls in my unit tests. This is working fine in many instances. I can stub out any of the functions I have written myself, as well as any calls to FreeRTOS functions. When I call these functions from within a test, it is the Fake that is called. However, I cannot get it to work with the STM32 HAL functions - these function calls always call the real function.

I am defining fakes in the test source file, as follows:

FAKE_VALUE_FUNC(BaseType_t, xTaskCreate, TaskFunction_t, const char *, configSTACK_DEPTH_TYPE, void *, UBaseType_t, TaskHandle_t *);
FAKE_VALUE_FUNC(HAL_StatusTypeDef, HAL_TIM_Base_Init, TIM_HandleTypeDef*);

Then the first test, which works just fine, is as follows:

TEST(HeaterTestGroup, WHEN_BothTaskCreationsFail_THEN_HeaterInitFails)
{
    ADC_HandleTypeDef TestADC;
    BaseType_t taskCreateOutcomes[2] = { pdFAIL, pdFAIL };
    SET_RETURN_SEQ(xTaskCreate, taskCreateOutcomes, 2);
    
    CHECK_EQUAL(Heater_init(&TestADC), ERROR);
}

The function under test, HeaterInit() calls two functions:

   InitialiseHeater1Task(hadc);
   InitialiseHeater2Task(hadc);

Each of which contains a similar call to create a FreeRTOS task:

taskCreationSuccess = xTaskCreate( ControlTask,                 // Function that implements the task. 
                                    "Heater1ControlTask",       // Text name for the task. 
                                    128,                        // Stack size in words, not bytes
                                    (void *) &heater1,          // Parameter passed into the task. 
                                    1,                          // Priority at which the task is created. 
                                    &taskHandle);               // Used to pass out the created task's handle. 

When I step through this code in debug mode, I can see that it is the fake version of xCreateTask which is being called, and the return sequence is exactly as I have specified.

However, the second test, which is not working, is as follows:

  TEST(HeaterTestGroup, InitPWMTest)
{
    TIM_HandleTypeDef pTimer;
    HAL_StatusTypeDef halReturnOK = HAL_OK;
    
    SET_RETURN_SEQ(HAL_TIM_Base_Init, &halReturnOK, 1);
    CHECK_EQUAL(InitialisePwmTimer(&pTimer), SUCCESS);
}

This function under test, contains the following:

ErrorStatus InitialisePwmTimer(TIM_HandleTypeDef* const pTimerHandle)
{
    ErrorStatus PWMInitState = SUCCESS;
    
    pTimerHandle->Init.Prescaler = 1;
    pTimerHandle->Init.CounterMode = TIM_COUNTERMODE_UP;
    pTimerHandle->Init.Period = PWMPeriod;
    pTimerHandle->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    pTimerHandle->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    
    if (HAL_TIM_Base_Init(pTimerHandle) != HAL_OK)
    {
        PWMInitState = ERROR;
    }

...

If I step through this test in debug mode, I can see that the function calls the real function, defined in stm32f1xx_hal_tim.c, instead of calling my Fake.

I cannot see any reason why one of these would work and the other would not. Could anyone suggest a reason?

Smoggie Tom
  • 306
  • 4
  • 12

1 Answers1

1

It seems that the issue is that you are calling the test function HAL_TIM_Base_Init (which you mocked) from another function - which I assume is located on ANOTHER c/cpp file and not in the same cpp file as where you defined FAKE_VALUE_FUNC(HAL_StatusTypeDef, HAL_TIM_Base_Init, TIM_HandleTypeDef*);

The issue is that by using FAKE_VALUE_FUNC you are declaring the mock function only locally in that file. However, the original function is still in your code. and is visible to all the files in your code that included the header file that declares the original function. So when you call InitialisePwmTimer you leave the test file and go "out" to the rest of the code where the original HAL_TIM_Base_Init prevails.

I offer two solutions:

  1. Exclude from your unit tests project the C file that contains the definition (implementation) of the original function and if that is not possible try just wrapping the function with #ifdef so that it won't be included in the unit test project. But in any case, keep the header file. Then replace the use of FAKE_VALUE_FUNC with DECLARE_FAKE_VALUE_FUNC and DEFINE_FAKE_VALUE_FUNC (read more about it here: FFF Primer). This will cause your mock function to take preference and be called from outside the test file as well.
  2. You can just use function pointer substitution. I wrote more extensively about it here: Function Pointer Substitution With FFF
Eyal Gerber
  • 1,026
  • 10
  • 27