You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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,步骤如下:

  1. 创建精简头文件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);
    
  2. 运行CMock生成脚本(需提前安装Ruby):
    ruby cmock/lib/cmock.rb -o test/mocks test/mocks/Mock_HAL_UART.h
    
    执行后会生成Mock_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:23:24