第五章:如何对 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 覆盖。

相关推荐
碎碎念_49219 小时前
以太网技术、VLAN、STP详解
网络·stp·vlan
电子工程师成长日记-C5119 小时前
51单片机智能灯光控制系统
单片机·嵌入式硬件·51单片机
hbugs00119 小时前
【案例分享】全网首个华三数据中心流量可视化实验,基于EVE-NG V7平台
网络·网络协议·安全·devops·eve-ng
狂奔蜗牛(bradley)20 小时前
嵌入式软件编程思想之事件驱动+表驱动状态机+事件参数+优先级FIFO
单片机·mcu
secondyoung20 小时前
Cortex-R52学习:存储系统
arm开发·单片机·学习·arm
不会C语言的男孩20 小时前
第 13 章 网络与分布式系统基础
网络
山东穆柯传感器21 小时前
安全触边损坏如何维修及更换配件
网络·安全
huainingning21 小时前
华三ACL单向TCP互通组网-通过Established状态回包实现
运维·网络·tcp/ip
Johnstons21 小时前
游戏网络测试怎么做?从延迟到丢包,一套完整的游戏弱网测试方案
网络·游戏·php
Rocket-Luo21 小时前
谈谈企业中的网络安全
网络·安全·web安全