提前认识软件开拓(19) C语言中的协议及单位测试示例
副标题#e#
在实际的软件开拓项目中,常常要实现多个模块之间的通信,这就需要各人约定好彼此之间的通信协议,各自凭据协议来收发息争析动静。
本文以实际的措施代码为例,具体先容了如何用C语言来实现通信协议,并基于对协议字段的判定,说明白措施单位测试的进程,为相关的开拓事情提供了有益的参考。
一、软件模块之间的协议
什么是软件模块之间的协议?差异的软件模块之间要实现彼此通信,就必需遵循配合的动静类型,各人凭据约定好的类型来收动员静。软件模块之间的协议就是差异模块间动静交互的类型。
在通信协议中,一条完整的动静由动静头和动静体组成,如图1所示。
图1 一条完整的动静示意图
在C语言中,用布局体来暗示协议。在进动作静理会的时候,一般只存眷动静体的内容。动静头只是用于标识一条动静,让其它模块可以或许识别该类动静。
二、单位测试
在提交措施版本之前,开拓人员需要对代码举办单位测试和集成测试。那么什么是单位测试呢?单位测试就是对措施中的一个函数举办测试,看对付某个输入,是否有预期的输出。
单位测试的示意图如图2所示。
图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;
在动静体的五个字段中,操纵范例、用户号码和操纵时间是本次要举办判定处理惩罚的字段,别的两个字段是保存字段,可以先不消赋详细的值。
在协议中,为什么要留有保存字段呢?这是利便今后对协议举办扩展。也就是说,假如今后除了操纵范例、用户号码和操纵时间之外,还需要增加新的字段界说,可以直接操作扩展字段。这在实际的软件开拓项目中是很重要的。
#p#副标题#e#
四、措施代码
基于以上协议,本文中的措施代码如下所示:
* 修改记录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; // 操纵范例, 操纵范例只能为1或2 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)) // 操纵范例只能为1或2, 其它为数据异常 { 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
#p#分页标题#e#
设定“操纵范例”为1,“用户号码”和“操纵时间”字段均切合协议要求。措施的执行环境如图3所示。
图3 “操纵范例”为1的正常执行环境
(2) “操纵范例”为2
#p#分页标题#e#
设定“操纵范例”为2,“用户号码”和“操纵时间”字段均切合协议要求。措施的执行环境如图4所示。
图4 “操纵范例”为2的正常执行环境
2. 异常测试用例
异常测试用例是指不满意措施输入条件的测试用例,即调查措施在错误的输入环境下,发生的功效是奈何的。
什么是异常测试?包罗了两种环境:
1) 输入正确的值,措施发生错误的输出。
2) 输入错误的值,措施发生正确的输出。
(1) “操纵范例”不为1或2
设定“操纵范例”为3(不为1或2的正整数),“用户号码”和“操纵时间”字段均切合协议要求。措施的执行环境如图5所示。
图5 “操纵范例”为3的异常执行环境
(2) “用户号码”不是8位
设定“操纵范例”为1,“用户号码”字段为9位,“操纵时间”字段切合协议要求。措施的执行环境如图6所示。
图6 “用户号码”为9位的异常执行环境
(3) “操纵时间”不是8位
设定“操纵范例”为1,“用户号码”切合协议要求,“操纵时间”字段为9位。措施的执行环境如图7所示。
图7 “操纵时间”为9位的异常执行环境
正常和异常测试的环境都有许多种,这里就纷歧一罗列了。为了确保措施的正确性,必然要对措施(可能函数)举办充实的单位测试。
七、总结
对付协议,这是差异模块之间通信的桥梁。因此,在开始编码之前,必然要将协议界说清楚,这样也可以淘汰后续修改带来的未便。
对付单位测试,这是每个软件开拓工程师都必需要当真看待的。单位测试举办得是否彻底,会直接影响到软件产物的质量。
本文以实际的措施代码为例子,对用C语言暗示协议和对代码举办单位测试作了具体的先容。文中涉及到的协议暗示要领和单位测试要领可供相关的软件开拓工程师参考。