软件开发中,每次需求的变更基本都需要改写代码,而代码变更后就需要进行功能测试,当然在功能测试之前需要代码的单元测试,避免代码改动后部分场景没有验证,最后出现各种问题。

通过测试框架快速完成代码的单元测试,不仅可以覆盖之前测试的场景,也能快速反应问题在哪里

分享正文之前推荐一个嵌入式招聘信息的平台:

数组初始化的三种方式_初始化数组_数组初始化c语言

常用的C语言测试框架有:

Unity示例

这里介绍Unity,其他的大家感兴趣可以自行查阅,不同的单元测试框架适用于不同的开发需求和场景。开发人员可以按照自己的项目要求选择最适合的框架。

Unity最小可以只用到几个文件即可完成,把Unity源码目录下的unity.c、unity.h、unity_internals.h三个文件复制至我们的工程目录下进行编译即可,然后在测试文件代码中包含unity.h

简单的示例

完成功能函数的验证

#include #include "unity.h"void 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:test/test.c:47:test_AddFun:PASS-----------------------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(" = %dn", *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("x ", 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 n", *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 n", *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 n", *pData);    // 弹出顶部元素    cotStack_Pop(&stack);    return 0;}

序列化/反序列化功能使用说明

可以定义一个公共头文件

#ifndef STRUCT_H#define STRUCT_H#include "serialize/serialize.h"COT_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.h"int 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: n");    for (int i = 0; i < length; i++)    {        printf("x %s", buf[i], (i + 1) % 16 == 0 ? "n" : "");    }    printf("n");    // 反序列化使用demo    test_t test2;           // COT_DEFINE_STRUCT_VARIABLE(test_t, test2);    COT_INIT_STRUCT_VARIABLE(test_t, test2);    test2.Parse(&test2, buf);    printf("val = %dn", test2.val1[0]);    printf("val = %dn", test2.val1[1]);    printf("val = %dn", test2.val2);    printf("val = %dn", test2.val3);    printf("val = %dn", test2.val4);    printf("val = %lfn", test2.val5);    printf("val = %dn", test2.val6);    printf("name = %sn", test2.szName);    printf("val = %lfn", test2.val7);    printf("val = %fn", test2.val8);    printf("name = %sn", test2.szName1);    return 0;}

声明:本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。

限时特惠:本站每日持续更新海量各大内部网赚创业教程,会员可以下载全站资源点击查看详情
站长微信:11082411

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。