Unity、CMock、Ceedling 是嵌入式 C 开发中单元测试领域的黄金组合(均由 Throw The Switch 社区维护),三者分工明确、无缝协作,核心解决嵌入式 C 代码“难测试、难模拟、难自动化”的问题。下面拆解三者的具体作用、定位和协作关系:
一、先理清核心定位(一句话总结)
|
工具 |
核心作用 |
类比 |
|
Unity |
纯 C 编写的单元测试断言库(最小核心) |
测试的“裁判”:判定代码结果是否符合预期 |
|
CMock |
基于 Unity 的模拟(Mock)框架 |
测试的“替身演员”:模拟依赖模块的行为 |
|
Ceedling |
基于 Unity+CMock 的自动化测试构建工具(工程管理) |
测试的“总导演”:一键管理编译、执行、报告 |
二、逐个拆解:具体作用+使用场景
1. Unity:嵌入式C的“断言神器”(最小依赖)
核心作用
Unity 是一套极简的 C 单元测试断言库,无第三方依赖(纯 C 实现),专门适配嵌入式场景(支持裸机、RTOS、不同编译器/架构),核心功能是:
- 提供丰富的断言宏,判定函数/模块的输出是否符合预期;
- 输出清晰的测试结果(成功/失败、失败原因、行号);
- 适配嵌入式特性(支持内存受限、无标准库的环境)。
具体用法
比如测试一个加法函数 int add(int a, int b):
#include "unity.h" // 引入Unity头文件
// 测试用例:正常加法
void test_add_normal(void) {
// 断言:add(1,2)的结果必须等于3,否则报错
TEST_ASSERT_EQUAL(3, add(1, 2));
// 其他断言:比如判定结果非空、地址相等、内存相等
TEST_ASSERT_NOT_NULL(&add);
TEST_ASSERT_GREATER_THAN(2, add(1, 2));
}
// 测试入口(Ceedling会自动生成main,裸机可手动写)
int main(void) {
UNITY_BEGIN(); // 初始化Unity
RUN_TEST(test_add_normal); // 执行测试用例
return UNITY_END(); // 输出测试结果
}
关键特性
- 支持跨平台:兼容 GCC/Clang/ARMCC/IAR 等嵌入式编译器;
- 极小体积:核心代码仅几百行,可直接嵌入裸机项目;
- 自定义输出:支持重定向打印(比如输出到串口、日志)。
2. CMock:解决嵌入式C的“依赖噩梦”(Mock框架)
核心痛点(为什么需要CMock)
嵌入式C代码大量依赖硬件寄存器、外设驱动、其他模块(比如:uart_send() 依赖串口硬件,sensor_read() 依赖传感器),直接测试会有两个问题:
- 无法“脱离硬件”测试(比如没有实物板卡时,无法测试依赖UART的代码);
- 依赖模块未开发完成时,被测试代码无法单独测试。
CMock的核心作用
CMock 是基于 Unity 的模拟(Mock)框架,能为被测试代码的“依赖模块”创建模拟替身(Mock函数),核心能力:
- 拦截对依赖函数的调用,返回预设的结果(无需真实硬件/模块);
- 验证依赖函数是否被调用、调用次数、入参是否符合预期;
- 自动生成Mock代码(无需手动写替身函数)。
具体用法
比如测试一个依赖 uart_send() 的函数 void report_error(int code)(功能:调用 uart_send 发送错误码):
// 被测试代码(report_error.c)
#include "uart.h"
void report_error(int code) {
uart_send(code); // 依赖uart模块的uart_send函数
}
// 测试用例(借助CMock模拟uart_send)
#include "unity.h"
#include "mock_uart.h" // CMock自动生成的mock_uart.h
void test_report_error(void) {
// 预设:当调用uart_send(100)时,无需真实串口,直接返回(模拟成功)
uart_send_Expect(100);
// 执行被测试函数
report_error(100);
// 验证:uart_send是否被调用了1次(CMock自动校验)
}
关键特性(嵌入式适配)
- 支持模拟“返回值、输出参数、void函数”;
- 支持“严格校验”(比如依赖函数必须按指定顺序调用);
- 适配C语言特性(支持函数指针、宏、不同调用约定)。
3. Ceedling:嵌入式C测试的“自动化管家”
核心痛点(为什么需要Ceedling)
仅用 Unity+CMock 时,测试流程仍需手动操作:
- 手动编写Makefile编译测试代码、链接Unity/CMock;
- 手动管理测试用例、依赖文件、Mock生成;
- 手动收集测试结果、生成报告。
Ceedling 是基于 Ruby 开发的自动化测试构建工具,核心作用是:
- 一键自动化:自动生成测试工程、编译代码、执行测试、生成报告;
- 无缝集成 Unity+CMock:自动生成Mock代码、自动链接Unity断言库;
- 工程化管理:支持测试用例组织、代码覆盖率统计、多配置切换(比如裸机/主机模拟)。
具体作用
初始化测试工程:
ceedling new my_project # 一键创建测试工程,自动包含Unity+CMock
添加被测试代码:
将 add.c、report_error.c 放入 src/ 目录,测试用例放入 test/ 目录;
一键执行测试:
ceedling test # 自动编译、生成Mock、执行所有测试用例、输出结果
ceedling test:test_add # 仅执行指定测试用例
生成测试报告:
自动生成 HTML/XML 格式的测试报告、代码覆盖率报告(比如哪些代码没被测试到)。
关键特性(嵌入式工程化)
- 支持裸机测试:可配置编译器(比如ARMCC)、链接脚本,直接测试裸机代码;
- 覆盖率统计:集成 gcov(主机)/gcovr(嵌入式),统计行覆盖率、分支覆盖率;
- 灵活配置:通过
project.yml配置Mock规则、编译选项、测试路径。
三、三者协作流程
编写被测试代码(src/)
↓
Ceedling 读取配置(project.yml)
↓
Ceedling 调用 CMock → 自动生成依赖模块的Mock代码
↓
Ceedling 编译:被测试代码 + 测试用例 + Unity + Mock代码
↓
执行测试 → Unity 断言判定结果(成功/失败)
↓
Ceedling 收集结果 → 生成测试报告/覆盖率报告
四、新手常见使用场景
|
场景 |
核心依赖工具 |
解决的问题 |
|
测试纯算法函数(无依赖) |
Unity |
验证算法逻辑是否正确 |
|
测试依赖硬件的函数(如UART) |
Unity+CMock |
脱离硬件,模拟UART的行为 |
|
自动化执行所有测试用例 |
Ceedling |
一键编译、执行、看报告 |
|
统计测试覆盖度(嵌入式裸机) |
Ceedling |
知道哪些代码没被测试到 |
|
持续集成(CI)中跑单元测试 |
Ceedling |
每次代码提交自动执行测试 |
五、避坑&总结
- 优先级:先学 Unity(核心断言)→ 再学 CMock(解决依赖)→ 最后学 Ceedling(自动化);
- 嵌入式适配:三者均支持裸机(无OS),只需在 Ceedling 配置中指定交叉编译器(比如ARM-GCC);
- 核心价值:让嵌入式C代码也能像高级语言(Python/Java)一样做“低成本、高效率”的单元测试,提前发现bug,减少联调/硬件测试阶段的问题。
简单说:Unity 管“判对错”,CMock 管“造替身”,Ceedling 管“自动化”,三者结合让嵌入式C单元测试从“难落地”变成“开箱即用”。
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_30446547/article/details/155781766



