STM32F4 Unity 单元测试与多次测试失败率统计详细步骤本文档介绍如何在 STM32F4 工程中移植 Unity 单元测试框架并实现多次循环测试、统计失败率的完整流程。1. 概述Unity 是一个轻量级的 C 语言单元测试框架非常适合在 STM32 等嵌入式平台上运行。本方案将 Unity 移植到 STM32F4通过串口输出测试结果并进一步实现多次重复运行所有测试用例统计总失败次数、用例失败率、迭代失败率通过全局变量控制是否输出详细测试过程避免串口刷屏2. 准备工作2.1 硬件STM32F4 开发板如 STM32F407 DiscoveryST-Link 调试器USB 转串口模块或使用板载虚拟串口串口调试助手如 SecureCRT、PuTTY、串口助手2.2 软件STM32CubeIDE / Keil MDK / IAR EWARM任选其一一个可正常编译、下载、串口打印的 STM32F4 基础工程Unity 源码从 GitHub 获取 ThrowTheSwitch/Unity3. 添加 Unity 到工程在工程根目录下新建Unity文件夹。将以下三个文件复制到Unity文件夹src/unity.csrc/unity.hsrc/unity_internals.h在 IDE 中将unity.c添加到编译列表并将Unity文件夹加入头文件搜索路径。推荐的文件结构示例YourProject/ ├── Core/ │ ├── Inc/ │ │ ├── main.h │ │ ├── unity_config.h │ │ └── my_module.h │ └── Src/ │ ├── main.c │ ├── my_module.c │ └── test_my_module.c ├── Unity/ │ ├── unity.c │ ├── unity.h │ └── unity_internals.h └── ...4. 配置 Unity4.1 创建unity_config.h在Core/Inc下新建unity_config.h内容如下#ifndefUNITY_CONFIG_H#defineUNITY_CONFIG_H#includemain.h// 包含 HAL 库和 huart1 声明externUART_HandleTypeDef huart1;externintunity_verbose;// 全局变量控制是否输出详细测试过程// 将 Unity 的字符输出重定向到串口#defineUNITY_OUTPUT_CHAR(a)do{\if(unity_verbose){\uint8_tch(uint8_t)(a);\HAL_UART_Transmit(huart1,ch,1,HAL_MAX_DELAY);\}\}while(0)#endif说明unity_verbose为 0 时Unity 内部的所有打印测试名称、成功/失败信息都会被丢弃只保留我们自身通过uart_printf输出的统计信息。需要调试时将其置 1 即可看到详细过程。4.2 让 Unity 识别配置文件在 IDE 的编译器预定义宏中添加UNITY_INCLUDE_CONFIG_H或者在unity_internals.h开头手动添加#defineUNITY_INCLUDE_CONFIG_H推荐使用编译器预定义方式避免修改 Unity 源码。4.3 实现setUp和tearDownUnity 要求必须定义这两个函数即使为空。可以在main.c或单独的unity_main.c中实现#includeunity.hvoidsetUp(void){// 每个测试用例执行前调用可在此初始化变量、复位外设等}voidtearDown(void){// 每个测试用例执行后调用可在此进行清理}5. 编写测试用例5.1 示例被测函数创建my_module.h和my_module.cmy_module.h#ifndefMY_MODULE_H#defineMY_MODULE_HintAddFunction(inta,intb);#endifmy_module.c#includemy_module.hintAddFunction(inta,intb){returnab;}5.2 创建测试文件创建test_my_module.c#includeunity.h#includemy_module.hvoidtest_AddFunction_Should_Return_Sum(void){TEST_ASSERT_EQUAL_INT(5,AddFunction(2,3));TEST_ASSERT_EQUAL_INT(-1,AddFunction(2,-3));}voidtest_AddFunction_With_Zero(void){TEST_ASSERT_EQUAL_INT(0,AddFunction(0,0));TEST_ASSERT_EQUAL_INT(10,AddFunction(0,10));}6. 单次测试运行可选验证在main.c中先实现单次运行确认 Unity 工作正常#includemain.h#includeunity.hvoidtest_AddFunction_Should_Return_Sum(void);voidtest_AddFunction_With_Zero(void);intmain(void){HAL_Init();SystemClock_Config();MX_USART1_UART_Init();unity_verbose1;// 打开详细输出UNITY_BEGIN();RUN_TEST(test_AddFunction_Should_Return_Sum);RUN_TEST(test_AddFunction_With_Zero);UNITY_END();while(1){}}烧录后打开串口助手波特率与工程一致如 115200应能看到类似输出---------------------- 2 Tests 0 Failures 0 Ignored OK7. 多次测试与失败率统计7.1 设计思路外层循环TEST_ITERATIONS次。每次循环调用UnityBegin()重置 Unity 状态运行所有测试再调用UnityEnd()获取本轮失败数。累加总失败次数、总执行用例次数、至少失败一次的迭代次数。最后计算并输出失败率。7.2 完整main.c示例#includemain.h#includeunity.h#includestdarg.h#includestdio.h#includestring.hexternUART_HandleTypeDef huart1;intunity_verbose0;// 默认关闭 Unity 详细输出// 自定义串口打印函数voiduart_printf(constchar*fmt,...){charbuf[128];va_list args;va_start(args,fmt);vsnprintf(buf,sizeof(buf),fmt,args);va_end(args);HAL_UART_Transmit(huart1,(uint8_t*)buf,strlen(buf),HAL_MAX_DELAY);}// 测试用例声明voidtest_AddFunction_Should_Return_Sum(void);voidtest_AddFunction_With_Zero(void);// 测试用例表typedefstruct{void(*func)(void);constchar*name;}test_case_t;statictest_case_ttest_suite[]{{test_AddFunction_Should_Return_Sum,test_AddFunction_Should_Return_Sum},{test_AddFunction_With_Zero,test_AddFunction_With_Zero},};#defineNUM_TEST_CASES(sizeof(test_suite)/sizeof(test_suite[0]))#defineTEST_ITERATIONS100// 运行一轮所有测试staticvoidrun_all_tests_once(void){for(size_ti0;iNUM_TEST_CASES;i){UnityDefaultTestRun(test_suite[i].func,test_suite[i].name,__LINE__);}}intmain(void){HAL_Init();SystemClock_Config();MX_USART1_UART_Init();uart_printf(开始多次测试迭代次数:%d\r\n,TEST_ITERATIONS);inttotal_failures0;inttotal_executions0;intfailed_iterations0;for(inti0;iTEST_ITERATIONS;i){UnityBegin(__FILE__);// 重置并开始本轮run_all_tests_once();// 运行所有测试intiter_failuresUnityEnd();// 结束并获取本轮失败数total_failuresiter_failures;total_executionsNUM_TEST_CASES;if(iter_failures0){failed_iterations;}// 每轮简单输出可选uart_printf(迭代 %3d: 失败 %d\r\n,i1,iter_failures);}// 最终统计uart_printf(\r\n 测试统计 \r\n);uart_printf(总迭代次数 : %d\r\n,TEST_ITERATIONS);uart_printf(总执行用例次数 : %d\r\n,total_executions);uart_printf(总失败次数 : %d\r\n,total_failures);uart_printf(用例失败率 : %.2f%%\r\n,(float)total_failures/total_executions*100.0f);uart_printf(迭代失败率 : %.2f%%\r\n,(float)failed_iterations/TEST_ITERATIONS*100.0f);while(1){}}7.3 输出结果示例开始多次测试迭代次数100 迭代 1: 失败 0 迭代 2: 失败 0 ... 迭代 99: 失败 1 迭代 100: 失败 0 测试统计 总迭代次数 : 100 总执行用例次数 : 200 总失败次数 : 1 用例失败率 : 0.50% 迭代失败率 : 1.00%8. 优化与注意事项8.1 控制 Unity 输出unity_verbose 0只输出自定义统计适合批量测试。unity_verbose 1输出 Unity 详细过程适合调试单个失败用例。若某轮出现失败可记录轮次之后单独将unity_verbose置 1 并减少迭代次数定位问题。8.2 测试用例列表化使用结构体数组管理测试用例新增用例只需在test_suite[]中添加一行无需修改循环逻辑。8.3 硬件相关测试如果测试涉及外设、中断、通信建议在setUp()中重新初始化相关外设。每轮迭代之间可加HAL_Delay(10)让系统稳定。避免在测试中执行不可逆操作如擦除 Flash。8.4 其他注意事项栈大小vsnprintf可能占用较多栈空间若栈较小可减小buf或改用简单的字符串发送。串口波特率建议 115200 或更高避免串口输出成为瓶颈。迭代次数根据测试耗时调整避免测试时间过长。浮点打印确保工程已启用浮点格式化支持如-u _printf_float。setUp/tearDown必须定义且要保证测试用例之间完全独立。9. 进阶PC 端与 CI对于纯逻辑代码更推荐在 PC 上使用 Ceedling 或 CMake Unity 运行测试速度快且易于集成 CI。STM32 上只保留少量硬件相关测试。PC 端多次测试示例Bashforiin$(seq1100);do./test_runner||echo迭代$i失败failures.logdone10. 总结通过以上步骤你可以在 STM32F4 上成功移植 Unity 单元测试框架。通过串口查看单次测试结果。实现多次循环测试并统计用例失败率和迭代失败率。通过unity_verbose灵活控制输出详细程度。使用测试用例表方便扩展测试套件。这套方案结构清晰、资源占用小适合在嵌入式开发中持续验证代码逻辑。