第五章:如何对 HAL 库本身进行单元测试?

这一章我们将深入嵌入式开发最"底层"的阵地。很多开发者会问:"逻辑层可以隔离,但我直接配置 STM32 寄存器或调用 HAL 库的代码,怎么测?"

这一章,我们要处理那些无法回避的底层调用,并引入 TDD 的进阶手段:Fake(伪函数)

5.1 为什么 Mock 有时不够用?

对于 HAL_GPIO_WritePin 这种简单的一对一调用,Expect 很好用。但如果你在写一个 I2C 扫描逻辑,代码里涉及大量的循环和连续调用,写几十个 Expect 会让测试脚本极其冗长且难以维护。

此时,我们需要 Fake(伪造实现):我们写一个功能简单的函数,替换掉真正的 HAL 函数,并让它记录下所有的动作。

5.2 实战:测试一个 I2C 设备探测器

需求: 扫描 I2C 总线上地址从 1127 的设备,并返回第一个响应的地址。

底层接口 (stm32g0xx_hal.h)HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);

5.3 编写测试:使用 Stub(存根/钩子)

Ceedling 的 CMock 支持 StubWithCallback。我们可以定义一个自定义函数,让 CMock 在被调用时运行我们的代码。

#include "unity.h"

#include "mock_stm32g0xx_hal_i2c.h" // 模拟 HAL 库

#include "I2C_Scanner.h"

// 这是一个伪造的 I2C 响应函数

// 我们模拟只有地址 0x50 的设备有响应

HAL_StatusTypeDef my_I2C_Stub(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout, int num_calls) {

if (DevAddress == (0x50 << 1)) {

return HAL_OK;

}

return HAL_ERROR;

}

void test_Scanner_Should_Return0x50_WhenDeviceFound(void) {

// 告诉 CMock:当 HAL_I2C_IsDeviceReady 被调用时,别管 Expect 了,去运行 my_I2C_Stub

HAL_I2C_IsDeviceReady_StubWithCallback(my_I2C_Stub);

uint8_t found_addr = I2C_Scanner_ScanFirst();

TEST_ASSERT_EQUAL_HEX8(0x50, found_addr);

}

5.4 进阶技巧:针对 STM32 寄存器的"影子内存"

如果你在写非常底层的代码(如手写寄存器驱动),可以利用一种叫 Shadow Register 的技术:

  1. 在测试环境中,定义一组全局变量(如 uint32_t V_GPIOA_ODR)。

  2. 在头文件中通过宏定义将 GPIOA->ODR 指向这个变量。

  3. 测试方法 :调用你的驱动函数,然后断言 TEST_ASSERT_EQUAL_HEX32(0x01, V_GPIOA_ODR)

这让你可以验证驱动程序是否真的改写了对应的寄存器位。

5.5 本章核心心法:什么时候该停止测试?

请注意:

  • 不要去测试 ST 官方的 HAL 库 :我们默认 HAL_GPIO_WritePin 是工作的。

  • 测试的是"调用行为":我们要测的是"当传感器数据溢出时,驱动是否正确调用了复位寄存器"。

  • 避免过度 Mock:如果你的测试代码比业务代码长 5 倍,说明你的函数拆分得还不够细。

本章小结

我们掌握了 StubCallback,这让我们能够处理复杂的循环调用和底层库交互。至此,你已经完成了从业务逻辑到硬件驱动的全路径 TDD 覆盖。

相关推荐
Zhan8611248 小时前
数据接口的序列号机制与丢包检测:西班牙行情数据IBEX指数实时行情接入笔记
大数据·数据结构·笔记·区块链
果丁智能14 小时前
智能锁赋能网约房民宿数字化管控:身份核验+远程授权,筑牢安全防线、降本增效
网络·数据库·人工智能·安全·智能家居
wp123_115 小时前
射频前端无源器件观察:Coilcraft WBC1-1TLC vs TONEVEE WBT1-1CT 国产与进口巴伦变压器的技术博弈
网络
Mr..Jackey15 小时前
瑞佑 RUI Builder 图形化 UI 设计工具
arm开发·人工智能·单片机·ui·人机交互·ra8889·lcd控制芯片
映翰通朱工16 小时前
工业4G网关无公网IP远程运维实战(内网终端异地访问方案)
运维·服务器·网络·安全·智能路由器
天南散修16 小时前
MT7916 BA流程
网络·驱动开发·wifi·802.11
Yang961117 小时前
多功能一体化,成都鼎讯 LDMN-JM1 满足石油煤矿设备检定与训练需求
网络·能源
IP老炮不瞎唠17 小时前
Python 价格监控如何实现?思路与实用方法分享
运维·服务器·网络
睡不醒男孩03082318 小时前
CLup 6.x 版本中针对StarRocks 存算一体集群的完整操作手册
java·服务器·网络·clup
退休倒计时18 小时前
【每日一题】LeetCode 53. 最大子数组和 TypeScript
数据结构·算法·leetcode·typescript