嵌入式代码单元测试如何完成?

软件开发中,每次需求的变更基本都需要改写代码,而代码变更后就需要进行功能测试,当然在功能测试之前需要代码的单元测试,避免代码改动后部分场景没有验证,最后出现各种问题。
通过测试框架快速完成代码的单元测试,不仅可以覆盖之前测试的场景,也能快速反应问题在哪里
常用的c语言测试框架有:
unity:一个小型的,开源的c语言测试框架,提供了用于测试的基本结构和函数。简单好用,常用于嵌入式系统开发。
cunit:一个面向c语言测试的框架,使用简单,支持自动化测试和手动测试。
check:适用于c语言的单元测试框架,使用简单,支持测试套件、测试用例的管理,便于维护测试组件。
google test:google推出的c++测试框架,支持c语言,可以跨平台,具有丰富的断言库和mocks。
cmocka:适用于c语言的单元测试框架,支持内存泄漏检测,支持mock函数和stub函数等高级用法。
criterion:基于c语言的单元测试框架,支持参数化测试和测试用例依赖,具有良好的性能和易用性。
unity示例
这里介绍unity,其他的大家感兴趣可以自行查阅,不同的单元测试框架适用于不同的开发需求和场景。开发人员可以按照自己的项目要求选择最适合的框架。
unity最小可以只用到几个文件即可完成,把unity源码目录下的unity.c、unity.h、unity_internals.h三个文件复制至我们的工程目录下进行编译即可,然后在测试文件代码中包含unity.h
https://github.com/throwtheswitch/unity/releases
简单的示例
完成功能函数的验证
#include #include unity.hvoid setup() {    // 这里可以放置每个测试用例运行前的初始化代码}void teardown() {    // 这里可以放置每个测试用例运行后的清理代码}int add(int a, int b){    return a + b;}void test_addfun(void){    test_assert_equal_uint(6, add(1, 5));    test_assert_equal_uint(4, add(-1, 5));    test_assert_equal_uint(-6, add(-1, -5));}int main(){    unity_begin();  // 启动测试    run_test(test_addfun);    unity_end();  // 结束测试    return 0;}    
通过串口或终端打印内容为:
c: est/test.c:47pass-----------------------1 tests 0 failures 0 ignoredok  
其中,unity_internals.h文件中可以修改输出终端,即unity_output_char宏的定义
/*------------------------------------------------------- * output method: stdout (default) *-------------------------------------------------------*/#ifndef unity_output_char  /* default to using putchar, which is defined in stdio.h */  #include   #define unity_output_char(a) (void)putchar(a)#else  /* if defined as something else, make sure we declare it here so it's ready for use */  #ifdef unity_output_char_header_declaration    extern void unity_output_char_header_declaration;  #endif#endif  
其中自定义实现的c语言扩展库(cot)的容器功能函数都已通过unity添加了对应的单元测试用例,链接:
https://gitee.com/const-zpc/cot  
轻量级通用扩展库
旨在打造一个c语言的通用扩展库。
介绍
支持多种容器实现,包括通用队列(包括不定长队列)、栈、双向链表和动态数组功能
双向链表节点可动态创建(需要在初始化分配内存)或静态添加 动态数组在初始化分配的内存中最大限度地使用,支持随机访问(连续地址)
支持定义序列化/反序列化的结构体功能
使用到了boost库中的pp库功能宏语法;确保两边都需要保持头文件结构体定义一致
移植了部分 c++ boost库中的pp库功能
通过宏语法实现复杂的宏语言,灵活进行使用,在编译的时候生成自己期望的代码
软件架构
目录说明
├─cot│ ├─include│ │ ├─container // 容器实现头文件│ │ ├─preprocessor // 移植boost库中的pp库头文件│ │ └─serialize // 序列化/反序列化实现头文件│ └─src│ ├─container // 容器实现源文件│ └─serialize // 序列化/反序列化实现源文件├─test│ ├─container // 容器实现测试代码│ └─serialize // 序列化/反序列化测试代码└─unity // 单元测试框架代码  
使用说明
容器类功能使用说明
双向链表使用方式demo
int main(){ cotlist_t list; cotlistitem_t nodebuf[10]; cotlist_init(&list, nodebuf, 10); int data1 = 10; int data2 = 20; int data3 = 30; // 头部增加元素 cotlist_pushfront(&list, &data1); // 尾部增加元素 cotlist_pushback(&list, &data2); // 插入元素 cotlist_insert(&list, cotlist_end(&list), &data3); // 使用迭代器遍历所有元素 for_list_each(item, list) { printf( = %d, *item_ptr(int, item)); } // 移除指定元素 cotlist_remove(&list, &data3); // 根据添加移除元素 cotlist_removeif(&list, onremovecondition); cotlist_t list2; cotlistitem_t nodebuf2[3]; cotlist_init(&list2, nodebuf2, 3); // 链表内存交换 cotlist_swap(&list1, &list2); return 0;}  
动态数组使用方式demo
int main(){ uint8_t buf[20]; cotvector_t vector; cotvector_init(&vector, buf, sizeof(buf), sizeof(uint32_t)); // 在尾部追加元素 uint32_t data = 42; cotvector_push(&vector, &data); data = 56; cotvector_push(&vector, &data); data = 984; cotvector_push(&vector, &data); // 插入元素 uint32_t arrdata[2] = {125, 656}; cotvector_insertn(&vector, 2, &arrdata, 2); // 删除两个元素 cotvector_removen(&vector, 1, 2); // 根据添加删除元素 cotvector_removeif(&vector, onvectorremovecondition); // 打印数组中的数据内容 for (int i = 0; i < cotvector_size(&vector); i++) { printf(%02x , cotvector_data(&vector)[i]); } return 0;}  
双向队列(定长fifo)使用方式demo
int main(){ uint8_t buf[10]; cotqueue_t queue; cotqueue_init(&queue, buf, sizeof(buf), sizeof(int)); // 在尾部追加元素 int data = 42; cotqueue_push(&queue, &data, sizeof(data)); data = 895; cotqueue_push(&queue, &data, sizeof(data)); // 访问元素 int *pdata = (int *)cotqueue_front(&queue); printf(val = %d , *pdata); // 弹出首个元素 cotqueue_pop(&queue); return 0;}  
队列(不定长fifo)使用方式demo
int main(){ uint8_t buf[10]; cotindqueue_t queue; cotindqueue_init(&queue, buf, sizeof(buf)); // 在尾部追加元素 char data = 42; cotindqueue_push(&queue, &data, sizeof(data)); int data1 = 80; cotindqueue_push(&queue, &data, sizeof(data1)); long data2 = -400; cotindqueue_push(&queue, &data, sizeof(data2)); // 访问元素 size_t length; int *pdata = (int *)cotindqueue_front(&queue, &length); printf(val = %d , *pdata, length); // 弹出首个元素 cotindqueue_pop(&queue); return 0;}  
单向栈使用方式demo
int main(){ uint8_t buf[10]; cotstack_t stack; cotstack_init(&stack, buf, sizeof(buf), sizeof(int)); // 在顶部追加元素 int data = 42; cotstack_push(&stack, &data, sizeof(data)); data = 895; cotqueue_push(&stack, &data, sizeof(data)); // 访问元素 int *pdata = (int *)cotstack_top(&stack); printf(val = %d , *pdata); // 弹出顶部元素 cotstack_pop(&stack); return 0;}  
序列化/反序列化功能使用说明
可以定义一个公共头文件
#ifndef struct_h#define struct_h#include serialize/serialize.hcot_define_struct_type(test_t, ((uint16_t) (val1) (2)) ((int32_t) (val2) (1)) ((uint8_t) (val3) (1)) ((int16_t) (val4) (1)) ((double_t) (val5) (1)) ((int16_t) (val6) (1)) ((string_t) (szname) (100)) ((double_t) (val7) (1)) ((float_t) (val8) (1)) ((string_t) (szname1) (100)))#endif // struct_h  
各个模块引用头文件使用
#include struct.hint main(){ uint8_t buf[100]; // 序列化使用demo cot_define_struct_variable(test_t, test); test.val1[0] = 5; test.val1[1] = 89; test.val2 = -9; test.val3 = 60; test.val4 = -999; test.val5 = 5.6; test.val6 = 200; test.val7 = -990.35145; test.val8 = -80.699; sprintf(test.szname, test56sgdgdfgdfgdf); sprintf(test.szname1, sdfsdf); int length = test.serialize(buf, &test); printf(serialize: ); for (int i = 0; i < length; i++) { printf(%02x %s, buf[i], (i + 1) % 16 == 0 ? : ); } printf(); // 反序列化使用demo test_t test2; // cot_define_struct_variable(test_t, test2); cot_init_struct_variable(test_t, test2); test2.parse(&test2, buf); printf(val = %d, test2.val1[0]); printf(val = %d, test2.val1[1]); printf(val = %d, test2.val2); printf(val = %d, test2.val3); printf(val = %d, test2.val4); printf(val = %lf, test2.val5); printf(val = %d, test2.val6); printf(name = %s, test2.szname); printf(val = %lf, test2.val7); printf(val = %f, test2.val8); printf(name = %s, test2.szname1); return 0;}  


全球智能音箱供应市场收入规模超百亿美元,年底保有量将达2.25亿台
Vivo Watch功能将是什么样的呢?
薄膜电容在新能源汽车中的应用分析
如何维修低音炮或放大器
光模块应用中高速TIA芯片的参数有哪些?
嵌入式代码单元测试如何完成?
4公里图传,26分钟续航,小米无人机高端版将发售
中国品牌崛起 IC设计成长强
2019第十二届国际物联网拉开帷幕:RFID机器人自动贴标系统
无锡ISO特气供应气站消防设备电源监控的设计与应用
红外线在医疗方面的应用
湖南电信与中兴通讯合作率先完成了基于vSTB方案的现网验证
脉搏传感器原理说明
5G巡检机器人上岗作业,智能巡检改变不止“亿”点点
云计算如何改变消费电子产品业务
HMD一周岁,12个月11款新品谱写诺基亚手机新篇章_软件APK曝光诺基亚多款新机在路上
AP7136 低压差线性稳压器 L-DO 3.6V
控制碱性蚀刻液的表面张力
安费诺智能连接器助力智慧城市
对电荷传输的新认识,一种奇异的量子力学机制