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?
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 definedFAKE_VALUE_FUNC(HAL_StatusTypeDef, HAL_TIM_Base_Init, TIM_HandleTypeDef*);The issue is that by using
FAKE_VALUE_FUNCyou 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 callInitialisePwmTimeryou leave the test file and go "out" to the rest of the code where the originalHAL_TIM_Base_Initprevails.I offer two solutions:
#ifdefso that it won't be included in the unit test project. But in any case, keep the header file. Then replace the use ofFAKE_VALUE_FUNCwithDECLARE_FAKE_VALUE_FUNCandDEFINE_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.