让你提前认识软件开发(19):C语言中的协议及单元測试演示样例

简介:

第1部分 又一次认识C语言

C语言中的协议及单元測试演示样例

 

【文章摘要】

        在实际的软件开发项目中。常常要实现多个模块之间的通信。这就须要大家约定好相互之间的通信协议,各自依照协议来收发和解析消息。

       本文以实际的程序代码为例,详细介绍了如何用C语言来实现通信协议,并基于对协议字段的推断,说明了程序单元測试的过程,为相关的开发工作提供了故意的參考。

【关键词】

       软件开发  协议  单元測试  C语言  字段

 

一、软件模块之间的协议

        什么是软件模块之间的协议?不同的软件模块之间要实现相互通信,就必须遵循共同的消息规范。大家依照约定好的规范来收发消息。软件模块之间的协议就是不同模块间消息交互的规范。

        在通信协议中,一条完整的消息由消息头和消息体构成,如图1所看到的。

一条完整的消息示意图

        在C语言中。用结构体来表示协议。在进行消息解析的时候,一般仅仅关注消息体的内容。

消息头仅仅是用于标识一条消息。让其他模块能够识别该类消息。

 

二、单元測试

       在提交程序版本号之前,开发者须要对代码进行单元測试和集成測试。

那么什么是单元測试呢?单元測试就是对程序中的一个函数进行測试。看对于某个输入,是否有预期的输出。

       单元測试的示意图如图2所看到的。

单元測试的示意图

        能够把函数看成一个灰色的盒子,測试的时候仅仅关心输入和输出,要设计多组单元測试数据来对函数的功能进行測试。

        此外,在測试中。另一个叫做“測试用例”的概念。測试用例就是一次測试的整个过程,包含:測试目的、预置条件、測试步骤、预期结果、通过准则、測试工具等。

 

三、本程序中的协议

        本程序中的协议包含了消息头和消息体。当中。消息头有四个字段。消息体有五个字段。

例如以下代码所看到的。

// 消息头结构

typedef struct

{

    UINT16  iReserve1;

    UINT16  iReserve2;

    UINT16  iReserve3;

    UINT16  iReserve4;

}MsgHead_T;

 

// 消息结构体(包含消息头和消息体)

typedef struct

{

    MsgHead_T    MsgHead;                       // 消息头

    UINT32       iOperType;                         // 操作类型

    UINT8        szUserNumber[30];            // 用户号码

    UINT8        szOperTime[20];                // 操作时间格式为: yyyymmdd

    UINT32       iReserve1;                          // 保留字段1

    UINT8        szReserve2[50];                 // 保留字段2

}UserReqMsg_T;

 

        在消息体的五个字段中。操作类型、用户号码和操作时间是本次要进行推断处理的字段。另外两个字段是保留字段,能够先不用赋详细的值。

        在协议中,为什么要留有保留字段呢?这是方便以后对协议进行扩展。

也就是说,假设以后除了操作类型、用户号码和操作时间之外,还须要添加新的字段定义。能够直接利用扩展字段。这在实际的软件开发项目中是非常重要的。

 

四、程序代码

        基于以上协议。本文中的程序代码例如以下所看到的:

/**********************************************************************

版权全部 (C)2014, Zhou Zhaoxiong。

*

文件名: UnitTest.c

文件标识:无

内容摘要:协议及单元測试演示样例代码

其他说明:无

当前版本号: V1.0

    者: Zhou Zhaoxiong

完毕日期: 20140507

*

改动记录1// 改动历史记录包含改动日期、版本号号、改动人及改动内容

改动日期:

版本号号:

改动人:

改动内容:

*

**********************************************************************/

#include <stdio.h>

#include <string.h>

 

// 重定义数据类型

typedef unsigned char       UINT8;

typedef unsigned short int    UINT16;

typedef unsigned int        UINT32;

typedef signed   int        INT32;

 

// 消息头结构

typedef struct

{

    UINT16  iReserve1;

    UINT16  iReserve2;

    UINT16  iReserve3;

    UINT16  iReserve4;

}MsgHead_T;

 

// 消息结构体(包含消息头和消息体)

typedef struct

{

    MsgHead_T   MsgHead;                // 消息头

    UINT32      iOperType;      // 操作类型操作类型仅仅能为12

    UINT8       szUserNumber[30];         // 用户号码

    UINT8       szOperTime[20];      // 操作时间格式为: yyyymmdd

    UINT32       iReserve1;                // 保留字段1

    UINT8        szReserve2[50];           // 保留字段2

}UserReqMsg_T;

 

// 函数声明

INT32 ProcUserReqMsg(UserReqMsg_T *ptUserReqMsg);

INT32 main();

 

/**********************************************************************

功能描写叙述:主函数

输入參数:无

输出參数:无

返回值: 0-运行完毕

其他说明:无

改动日期        版本号号              改动人         改动内容

* --------------------------------------------------------------------------------------------------

* 20140507         V1.0                zzx            创建

***********************************************************************/

INT32 main()

{

    UINT8  iRetVal          = 0;

    UINT32 iOperType        = 0;        // 操作类型

    UINT8  szUserNumber[30] = {0};      // 用户号码

    UINT8  szOperTime[10]  = {0};    // 操作时间格式为: yyyymmdd

 

    UserReqMsg_T tUserReqMsg = {0};     // 请求消息

 

    // 对消息头部进行赋值

    tUserReqMsg.MsgHead.iReserve1 = 1;

    tUserReqMsg.MsgHead.iReserve2 = 2;

    tUserReqMsg.MsgHead.iReserve3 = 3;

    tUserReqMsg.MsgHead.iReserve4 = 4;

 

    // 读入详细消息字段的值

    printf("操作类型: \n");

    scanf("%d", &iOperType);

    printf("用户号码: \n");

    scanf("%s", szUserNumber);

    printf("操作时间: \n");

    scanf("%s", szOperTime);

 

    // 对详细消息字段进行赋值(保留字段可不赋值)

    tUserReqMsg.iOperType = iOperType;

    strncpy(tUserReqMsg.szUserNumber, szUserNumber, strlen(szUserNumber));// 获取号码strncpy取代strcpy

    strncpy(tUserReqMsg.szOperTime,   szOperTime,   strlen(szOperTime));     // 获取时间strncpy取代strcpy

 

    // 对消息体的字段进行异常推断

    iRetVal = ProcUserReqMsg(&tUserReqMsg);  // 注意传递參数的时候要加上&

    if (iRetVal == 0)      // 函数运行正确

    {

        // 打印消息字段内容

        printf("The user request message is: iOperType=%d, szUserNumber=%s, szOperTime=%s.\n", tUserReqMsg.iOperType, tUserReqMsg.szUserNumber, tUserReqMsg.szOperTime);

        return 0;

    }

    else         // 打印异常消息

    {

        printf("Some content of the user request message is wrong, please check!\n");

        return -1;

    }

}

 

 

/**********************************************************************

功能描写叙述:对消息体的字段进行异常推断

输入參数: ptUserReqMsg-用户请求消息

输出參数:无

返回值: 0-成功   其他-失败

其他说明:无

改动日期        版本号号              改动人         改动内容

* --------------------------------------------------------------------------------------------------

* 20140507         V1.0                zzx            创建

***********************************************************************/

INT32 ProcUserReqMsg(UserReqMsg_T *ptUserReqMsg)

{

    INT32  iRetValue      = 0;

 

    // 对输入參数进行异常推断

    if (ptUserReqMsg == NULL)

    {

        printf("ProcUserReqMsg(...): input parameter(ptUserReqMsg) is NULL.\n");

 

        return -1;

    }

 

    // 对消息体字段进行异常推断

    if ((ptUserReqMsg->iOperType != 1) && (ptUserReqMsg->iOperType != 2))    // 操作类型仅仅能为12, 其他为数据异常

    {

        printf("ProcUserReqMsg(...): the iOperType is wrong, iOperType=%d.\n", ptUserReqMsg->iOperType);

 

        return -2;

    }

   

    if (strlen(ptUserReqMsg->szUserNumber) != 8) // 用户号码异常长度8位才正确

    {

        printf("ProcUserReqMsg(...): the szUserNumber is wrong.\n");

 

        return -3;

    }

 

    if (strlen(ptUserReqMsg->szOperTime) != 8)  // 操作时间异常长度8位才正确

    {

        printf("ProcUserReqMsg(...): the szOperTime is wrong.\n");

       

        return -4;

    }

 

    return 0;

}

 

       本程序要对ProcUserReqMsg函数进行单元測试。看该函数是否能对消息体的字段进行异常推断。

 

五、单元測试用例

1. 正常測试用例

       正常測试用例是指满足程序输入条件的測试用例。即观察程序在正确的输入情况下,是否能产生正确的输出。

       什么是正常測试?包含了两种情况:
        1) 输入正确的值,程序产生正确的输出。
        2) 输入错误的值。程序产生错误的输出。

       (1) “操作类型”为1

       设定“操作类型”为1,“用户号码”和“操作时间”字段均符合协议要求。程序的运行情况如图3所看到的。

“操作类型”为1的正常运行情况

 

        (2) “操作类型”为2

        设定“操作类型”为2,“用户号码”和“操作时间”字段均符合协议要求。程序的运行情况如图4所看到的。

“操作类型”为2的正常运行情况

 

2. 异常測试用例

        异常測试用例是指不满足程序输入条件的測试用例,即观察程序在错误的输入情况下。产生的结果是如何的。

        什么是异常測试?包含了两种情况:
        1) 输入正确的值,程序产生错误的输出。


        2) 输入错误的值,程序产生正确的输出。

        (1) “操作类型”不为12

        设定“操作类型”为3(不为12的正整数),“用户号码”和“操作时间”字段均符合协议要求。程序的运行情况如图5所看到的。

“操作类型”为3的异常运行情况

 

        (2) “用户号码”不是8

        设定“操作类型”为1,“用户号码”字段为9位。“操作时间”字段符合协议要求。程序的运行情况如图6所看到的。

“用户号码”为9位的异常运行情况

 

        (3) “操作时间”不是8

        设定“操作类型”为1,“用户号码”符合协议要求,“操作时间”字段为9位。程序的运行情况如图7所看到的。

“操作时间”为9位的异常运行情况

        正常和异常測试的情况都有非常多种,这里就不一一列举了。

为了确保程序的正确性,一定要对程序(或者函数)进行充分的单元測试。

 

七、总结

        对于协议。这是不同模块之间通信的桥梁。因此,在開始编码之前,一定要将协议定义清楚,这样也能够降低兴许改动带来的不便。

        对于单元測试,这是每一个软件开发project师都必须要认真对待的。

单元測试进行得是否彻底。会直接影响到软件产品的质量。

        本文以实际的程序代码为样例。对用C语言表示协议和对代码进行单元測试作了详细的介绍。

文中涉及到的协议表示方法和单元測试方法可供相关的软件开发project师參考。






本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5182254.html,如需转载请自行联系原作者

相关文章
|
1月前
|
缓存 运维 数据库
【测试人员兼职指南】利用专业技能:如何从测试转向开发赚钱
本文分享了作者作为测试人员如何利用专业技能转向开发来兼职赚钱的经验,包括分析和解决登录页面跳转、避免重复账号注册、用户登录后首页显示用户名以及添加退出功能等问题,并提供了Django项目中使用sqlite3数据库和后台管理的扩展技巧。
42 1
【测试人员兼职指南】利用专业技能:如何从测试转向开发赚钱
|
1月前
|
Java 测试技术 开发者
在软件开发中,测试至关重要,尤以单元测试和集成测试为然
在软件开发中,测试至关重要,尤以单元测试和集成测试为然。单元测试聚焦于Java中的类或方法等最小单元,确保其独立功能正确无误,及早发现问题。集成测试则着眼于模块间的交互,验证整体协作效能。为实现高效测试,需编写可测性强的代码,并选用JUnit等合适框架。同时,合理规划测试场景与利用Spring等工具也必不可少。遵循最佳实践,可提升测试质量,保障Java应用稳健前行。
37 1
|
16天前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
78 7
Jmeter实现WebSocket协议的接口测试方法
|
3天前
|
测试技术 持续交付 UED
软件测试的艺术与科学:平衡创新与质量的探索在软件开发的波澜壮阔中,软件测试如同灯塔,指引着产品质量的方向。本文旨在深入探讨软件测试的核心价值,通过分析其在现代软件工程中的应用,揭示其背后的艺术性与科学性,并探讨如何在追求技术创新的同时确保产品的高质量标准。
软件测试不仅仅是技术活动,它融合了创造力和方法论,是软件开发过程中不可或缺的一环。本文首先概述了软件测试的重要性及其在项目生命周期中的角色,随后详细讨论了测试用例设计的创新方法、自动化测试的策略与挑战,以及如何通过持续集成/持续部署(CI/CD)流程优化产品质量。最后,文章强调了团队间沟通在确保测试有效性中的关键作用,并通过案例分析展示了这些原则在实践中的应用。
15 1
|
1天前
|
测试技术 UED 开发者
软件测试的艺术:从代码审查到用户反馈的全景探索在软件开发的宇宙中,测试是那颗确保星系正常运转的暗物质。它或许不总是站在聚光灯下,但无疑是支撑整个系统稳定性与可靠性的基石。《软件测试的艺术:从代码审查到用户反馈的全景探索》一文,旨在揭开软件测试这一神秘面纱,通过深入浅出的方式,引领读者穿梭于测试的各个环节,从细微处着眼,至宏观视角俯瞰,全方位解析如何打造无懈可击的软件产品。
本文以“软件测试的艺术”为核心,创新性地将技术深度与通俗易懂的语言风格相结合,绘制了一幅从代码审查到用户反馈全过程的测试蓝图。不同于常规摘要的枯燥概述,这里更像是一段旅程的预告片,承诺带领读者经历一场从微观世界到宏观视野的探索之旅,揭示每一个测试环节背后的哲学与实践智慧,让即便是非专业人士也能领略到软件测试的魅力所在,并从中获取实用的启示。
|
24天前
|
缓存 网络协议 网络性能优化
C语言 网络编程(二)TCP 协议
TCP(传输控制协议)是一种面向连接、可靠的传输层协议,通过校验和、序列号、确认应答等机制确保数据完整性和可靠性。通信双方需先建立连接,再进行通信,采用三次握手建立连接,四次挥手断开连接。TCP支持任意字节长度的数据传输,具备超时重传、流量控制及拥塞控制机制。三次握手用于同步序列号和确认双方通信能力,四次挥手则确保双方均能完成连接关闭操作,保证数据传输的可靠性。
|
24天前
|
网络协议 视频直播 C语言
C语言 网络编程(三)UDP 协议
UDP(用户数据报协议)是一种无需建立连接的通信协议,适用于高效率的数据传输,但不保证数据的可靠性。其特点是无连接、尽力交付且面向报文,具备较高的实时性。UDP广泛应用于视频会议、实时多媒体通信、直播及DNS查询等场景,并被许多即时通讯软件和服务(如MSN/QQ/Skype、流媒体、VoIP等)采用进行实时数据传输。UDP报文由首部和数据部分组成,首部包含源端口、目的端口、长度和校验和字段。相比TCP,UDP具有更高的传输效率和更低的资源消耗。
|
1月前
|
测试技术 API
软件测试:Postman 工具的使用。开发及测试均需要掌握的测试工具
这篇文章详细介绍了Postman工具的各个模块功能,包括创建请求、集合、环境、自动化测试等,并解释了如何使用Postman进行GET、POST、PUT和DELETE等常见HTTP请求的测试。
|
1月前
|
运维 Kubernetes 监控
|
1月前
|
机器学习/深度学习 人工智能
高于临床测试3倍准确率!剑桥大学开发AI模型,提前6年预测阿尔茨海默症
【8月更文挑战第9天】剑桥大学研发的人工智能模型在预测阿尔茨海默症方面取得突破,准确率比传统临床测试高三倍,能提前六年预测疾病发生。该模型基于深度学习,利用大量临床及神经影像数据识别生物标志物,预测准确性达80%。这一成果有望促进早期干预,改善患者预后,但仍需更大规模研究验证,并解决隐私与公平性等问题。论文已发表于《The Lancet》子刊。
37 6