关注

32.Unity、CMock、Ceedling三件套

        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.creport_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

每次代码提交自动执行测试

五、避坑&总结

  1. 优先级:先学 Unity(核心断言)→ 再学 CMock(解决依赖)→ 最后学 Ceedling(自动化);
  2. 嵌入式适配:三者均支持裸机(无OS),只需在 Ceedling 配置中指定交叉编译器(比如ARM-GCC);
  3. 核心价值:让嵌入式C代码也能像高级语言(Python/Java)一样做“低成本、高效率”的单元测试,提前发现bug,减少联调/硬件测试阶段的问题。

        简单说:Unity 管“判对错”,CMock 管“造替身”,Ceedling 管“自动化”,三者结合让嵌入式C单元测试从“难落地”变成“开箱即用”。

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_30446547/article/details/155781766

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--