如何使用CMock与Unity框架对STM32的HAL_UART_Receive()进行Mock/Stub?
使用CMock和Unity模拟STM32 HAL_UART_Receive函数的实操步骤
不管是做Mock(验证调用行为)还是Stub(仅模拟返回),核心都是在主机端单元测试中替换真实的HAL函数,避免依赖硬件。以下是具体步骤:
1. 环境准备
- 下载并放置Unity(单元测试框架)和CMock(Mock生成工具)到项目目录,比如
./unity和./cmock。 - 确保持有STM32 HAL库的头文件(如
stm32f4xx_hal_uart.h),并将其路径加入编译包含目录。 - 推荐项目结构:
project/ ├── src/ # 被测业务代码 ├── test/ # 测试代码 │ └── mocks/ # CMock生成的Mock文件 ├── unity/ # Unity框架 └── cmock/ # CMock工具
2. 生成HAL_UART_Receive的Mock(用于验证调用细节)
CMock通过Ruby脚本生成Mock,步骤如下:
- 创建精简头文件
test/mocks/Mock_HAL_UART.h,仅包含需要Mock的函数声明:#include "stm32f4xx_hal.h" HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); - 运行CMock生成脚本(需提前安装Ruby):
执行后会生成ruby cmock/lib/cmock.rb -o test/mocks test/mocks/Mock_HAL_UART.hMock_HAL_UART.c和Mock_HAL_UART.h,内含CMock自动生成的Mock宏(如HAL_UART_Receive_ExpectAndReturn)。
3. 编写Mock测试用例
假设被测代码是调用HAL_UART_Receive的串口读取函数read_uart_byte:
// src/uart_reader.c #include "stm32f4xx_hal.h" #include "uart_reader.h" #define BLUETOOTH_RX_TIMEOUT 1000 uint8_t read_uart_byte(UART_HandleTypeDef *huart) { uint8_t temp; HAL_StatusTypeDef status = HAL_UART_Receive(huart, &temp, 1, BLUETOOTH_RX_TIMEOUT); return (status == HAL_OK) ? temp : 0xFF; }
对应的测试用例文件test/test_uart_reader.c:
#include "unity.h" #include "Mock_HAL_UART.h" #include "uart_reader.h" #include "stm32f4xx_hal.h" // 模拟被测代码中用到的huart5句柄 UART_HandleTypeDef huart5; void setUp(void) { // 每次测试前重置Mock,清除之前的调用期望 HAL_UART_Receive_Reset(); } void tearDown(void) { // 测试后无需额外清理(CMock会自动处理) } // 测试读取成功的场景 void test_read_uart_byte_success(void) { uint8_t expected_byte = 0x42; // 设置Mock期望:匹配指定参数,返回HAL_OK,并将pData指向的内存设为expected_byte HAL_UART_Receive_Expect(&huart5, NULL, 1, BLUETOOTH_RX_TIMEOUT); HAL_UART_Receive_IgnoreArg_pData(); // 忽略pData的具体地址(无需严格匹配时使用) HAL_UART_Receive_SetArgAddress_pData(&expected_byte); // 设置pData指向的内容 HAL_UART_Receive_Return(HAL_OK); uint8_t result = read_uart_byte(&huart5); TEST_ASSERT_EQUAL(expected_byte, result); } // 测试超时失败的场景 void test_read_uart_byte_timeout(void) { // 设置Mock期望:调用时返回HAL_TIMEOUT HAL_UART_Receive_ExpectAndReturn(&huart5, NULL, 1, BLUETOOTH_RX_TIMEOUT, HAL_TIMEOUT); HAL_UART_Receive_IgnoreArg_pData(); uint8_t result = read_uart_byte(&huart5); TEST_ASSERT_EQUAL(0xFF, result); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_read_uart_byte_success); RUN_TEST(test_read_uart_byte_timeout); return UNITY_END(); }
4. 编写Stub(仅模拟返回,无需验证调用)
如果不需要验证函数的调用次数、参数,仅需固定返回值或行为,直接编写Stub函数即可:
// test/stubs/Stub_HAL_UART.c #include "stm32f4xx_hal.h" HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { // 模拟固定返回HAL_OK,并填充pData if (pData != NULL && Size >= 1) { *pData = 0x12; // 固定返回的字节值 } return HAL_OK; }
测试时链接这个Stub文件,而非Mock文件即可。
5. 编译并运行测试
用主机端GCC编译(无需STM32交叉编译器,因为是主机单元测试):
gcc -o test_uart_reader \ src/uart_reader.c \ test/test_uart_reader.c \ test/mocks/Mock_HAL_UART.c \ unity/src/unity.c \ cmock/src/cmock.c \ -I. -Isrc -Itest -Iunity/src -Icmock/src -I/path/to/stm32/hal/headers
运行生成的可执行文件,查看Unity输出的测试结果:
./test_uart_reader
内容的提问来源于stack exchange,提问作者race_max
相关产品推荐
相关产品推荐

