第十章:TDD部署 —— Ceedling 环境的深度集成

1. 核心工具链安装(Windows 环境)

嵌入式开发者大多在 Windows 下工作,建议采用以下路径:

  1. Ruby : 下载安装 RubyInstaller with Devkit

    • 关键点 :安装后运行 ridk install,选择 1, 2, 3 来配置 MSYS2 开发环境。
  2. Ceedling: 打开终端(CMD 或 PowerShell):gem install ceedling

  3. GCC : 确保 gcc 在你的系统路径中。你可以用 MinGW,或者直接使用 STM32CubeIDE 自带的 arm-none-eabi-gcc(注:但在 PC 上跑单元测试,我们通常直接用 MinGW 的 gcc 编译成原生 exe)。

2. 将 Ceedling 植入 STM32 工程

假设你的工程名叫 MyProject,在根目录下运行:

ceedling new .

注意: 这里的 . 会在当前目录生成文件,而不是新建文件夹。它会生成:

  • project.yml:核心配置。

  • vendor/:里面包含了 Unity、CMock 和 Ceedling 的源码。这意味着你的项目是自包含的,别人拉下代码不需要再装 Ruby 也能跑(只要有 GCC 和 Ruby 环境)。

3. 深度定制 project.yml

这是部署最关键的一步。为了让 Ceedling 找到 STM32 的代码,你需要修改几个关键点:

A. 路径映射

STM32CubeIDE 的代码通常在 Core/Src,你得告诉 Ceedling:

:paths:

:test:

  • +:test/**

:source:

  • Core/Src/**

:include:

  • Core/Inc/**

  • Drivers/CMSIS/Device/ST/STM32G0xx/Include # 必须加上,否则找不到寄存器定义

B. CMock 自动化配置

这是让 Mock 变得好用的秘诀:

:cmock:

:mock_prefix: mock_

:when_no_prototypes: :warn

:enforce_strict_ordering: TRUE # 强制检查函数调用顺序,对协议栈测试非常有用

:plugins:

  • :ignore

  • :expect_any_args

  • :return_thru_ptr # 关键:用于 Mock 那些通过指针返回数据的函数(如 HAL_UART_Receive)

4. "硬件库"处理:Dummy Header 技巧

这是部署中最硬核的一点: STM32 的一些底层头文件包含了很多只有 ARM 编译器才认识的指令(如 __PACKED__IO)。原生 GCC 编译测试时会报错。

解决方案:test/support 文件夹下创建一个 stm32_fix.h,定义这些宏:

#ifndef STM32_FIX_H

#define STM32_FIX_H

#define __IO volatile

#define __PACKED

// 其他报错的宏统统在这里定义为空

#endif

然后在 project.yml:test_preprocess 里包含它。

5. 验证部署是否成功

在命令行输入:

ceedling summary

如果你能看到:

Project: MyProject

Test Files: 0

Total Tests: 0

说明你的环境已经原地起飞

附录:STM32 项目专属 project.yml 完整模板

这份配置文件针对 STM32 的目录结构和 C 语言特性进行了深度优化。建议将此内容保存在项目根目录。

--- Ceedling Project Configuration ---

:project:

:use_exceptions: FALSE

:use_test_preprocessor: TRUE # 开启预处理,解决复杂宏定义问题

:use_auxiliary_list: FALSE

:build_root: build # 所有的测试编译产物都在这里

:test_file_prefix: test_

:release_build:

:output: build/release/MyProject.out

:environment: \[\]

:extension:

:executable: .exe

:paths:

:test:

  • +:test/**

:source:

  • Core/Src/**

:include:

  • Core/Inc/**

  • Drivers/CMSIS/Device/ST/STM32G0xx/Include

  • Drivers/CMSIS/Include

  • test/support # 存放修复硬件宏定义的头文件

:defines:

模拟在 PC 环境下运行,定义一些 STM32 必要的宏

:common: &common_defines

  • STM32G031xx

  • UNIT_TEST

:test:

  • *common_defines

:test_preprocess:

  • *common_defines

:cmock:

:mock_prefix: mock_

:when_no_prototypes: :warn

:enforce_strict_ordering: TRUE

:plugins:

  • :ignore

  • :expect_any_args

  • :return_thru_ptr # 处理 HAL 库指针传参的关键插件

  • :callback # 处理中断回调测试的关键插件

:treat_as:

uint8: HEX8

uint16: HEX16

uint32: UINT32

int8: INT8

bool: UINT8

编译标志:针对 GCC 环境优化

:flags:

:test:

:compile:

:executable: gcc

:args:

  • -Wall

  • -Wno-address

  • -std=c99

  • -g

:plugins:

:load_paths:

  • "#{Ceedling.linker.tools_path}"

:enabled:

  • stdout_pretty_tests_report

  • module_generator

  • gcov # 开启覆盖率分析

总结:TDD 的核心

  • 隔离是第一生产力:无法测试是因为耦合太深。解耦不仅是为了测试,更是为了模块化。

  • 不要过度 Mock:只 Mock 你的被测对象(SUT)之外的东西。如果一个函数纯粹是逻辑运算,直接测,别 Mock。

  • 测试是最好的文档 :半年后你忘记了某个协议怎么解析,看一眼 test_protocol.c 里的 Expect 序列,你就全明白了。

相关推荐
国科安芯2 小时前
ASC0101S自动双向电平转换原理深剖
单片机·嵌入式硬件·fpga开发·云计算·信息与通信
李永奉2 小时前
中科蓝讯SDK开发-开启串口Debug打印调试教程
单片机·嵌入式硬件·mcu·物联网·电脑
LCG元2 小时前
STM32硬件SPI驱动OLED SSD1306显示屏全流程实战:从CubeMX配置到中英文显示
stm32·单片机·嵌入式硬件
飞凌嵌入式3 小时前
性能翻倍·功耗减半!RK3572八核核心板,重构嵌入式计算新标杆
人工智能·嵌入式硬件·飞凌嵌入式
魔术师Dix3 小时前
StartGame:Unity TDD 外部工程指南
学习·游戏·unity·c#·测试驱动开发
zcmodeltech3 小时前
智慧农业沙盘模型物联网控制系统设计:基于STM32与Modbus RTU的传感器-执行器闭环方案
服务器·分布式·stm32·嵌入式硬件·物联网·能源
WD1372980155714 小时前
WD5030L,超高集成度、大电流输出、高效率低发热,限流24V转12V,6A
stm32·单片机·嵌入式硬件·51单片机
~光~~16 小时前
【xv6学习】L0_环境配置
单片机·嵌入式硬件·学习
映翰通网络18 小时前
TI开发板上的边缘AI工具链:先认清OpenCV、GStreamer和TIDL
人工智能·嵌入式硬件·opencv·计算机视觉·ti
论迹复利19 小时前
Smith 圆图实战——从原理到 L 型匹配网络设计
嵌入式硬件·无线