前言
嵌入式行业摸爬滚打这几年,碰见有规范单元测试的项目寥寥无几。归根到底,无非是公司但愿快速迭代出产品,有问题等客户反馈再说。固然,也有人认为是嵌入式行业都是小而美的产品居多,没有到必定量级以前,玩不起单元测试这种配置。正如作个蛋炒饭,并不须要安排主厨、二厨通常。linux
不过出于对代码稳定性的追求,我认为仍是应该着手了解一下单元测试的。毕竟,这是有效提升代码说服力的方式之一。编程
相信没有真正体验过单元测试好处的读者一看到"单元测试"这几个字,可能会出现如下两种反应之一:c#
因为没有单元测试的经验,所以对采用这一方法去保证软件质量很好奇,也迫切地想要了解这一方法在项目中的实施性能优化
曾经使用单元测试但效果很差,由于在嵌入式行业,时常要跟硬件打交道,单元测试很难检测硬件问题,因此每每一看到"单元测试"这几个字的反应就是"没用"网络
若是读者是第一种反应那很好,本文就是科普单元测试的基本要点。若是读者是第二种反应,那多是对单元测试存在偏见,本系列文章也会介绍mock测试、错误注入等方式,使单元测试也能合理使用于嵌入式行业。框架
01函数
单元测试真的"无用"?性能
形成"单元测试无用论"的第一个缘由是,运用这一方法的时机不恰当。很多项目在一开始真正关心质量的人不多,更谈不上采用一整套的方法论去保证质量了。产品在开发出来后发现处处存在问题,只会拆西墙补东墙根本就不能阻止问题一而再,再而三地出现。因而,开始想起单元测试。一声令下,整个项目开始作单元测试。单元测试以模块为单位,须要先把项目拆分出来。若是你的项目代码总体耦合程度较高的话,单元测试根本无从提及,拆分的工做会让你痛苦不已。
单元测试
单元测试是一项耗时的工做,但管理者却每每但愿在短时间内看到效果。或者单元测试还没作到位管理层就等不及了,催你立刻开始下一步的开发,结果只能是前功尽弃。正确的作法是:在项目的开始之初就引入单元测试。对于之前没有部署单元测试的项目,先只对新增长的、相对独立的模块作单元测试、并逐渐覆盖老代码。学习
第二个致使"单元测试无用论"的缘由是,方法没有运用到位。要保证单元测试的有效性必定要引入另外一个概念--代码覆盖。关于代码覆盖,我之后会另外再写一篇文章介绍。只有将单元测试和代码覆盖结合在一块儿,综合使用才能保证单元测试的效果。
02
最原始的"单元测试"
这里给读者展现一下,不使用任何单元测试框架时,是怎么作单元测试的。
下面简单以linux内核链表为例:
struct list_head { struct list_head *next, *prev; }; /*定义一个结构体,只含有表示前驱和后继的指针,它就是咱们的主角了*/ #define LIST_HEAD_INIT(name) { &(name), &(name) } /*静态初始化*/ #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) /*动态初始化*/ static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } /*插入操做*/ /*删除操做*/ /*合并操做*/ ...
完整代码很长,这里没有必要所有贴出,能起演示做用就足够了。
如今就以INIT_LIST_HEAD函数为例,来考虑如何为这个函数设计测试用例。INIT_LIST_HEAD函数的实现是如此的简单,以致于很容易让人以为为它设计单元测试是多余的。可是,从单元测试的角度看,只要不存在可行性问题就不该考虑由于简单而不对其进行验证。并且,放弃对之进行验证,之后会下降代码覆盖率。
作单元测试须要经过编写程序的方式来完成,所编写的用于测试的代码又称为单元测试用例。
下面咱们来简单实现一个INIT_LIST_HEAD函数的测试用例:
int main(int argc,char **argv) { struct list_head list; /*避免函数没有使用参数而引起waining*/ UNUSED(argc); UNUSED(argv); list.prev = (struct list_head*)0xaaaa; list.next = (struct list_head*)0xbbbb; INIT_LIST_HEAD(list); /*检查前指针*/ if(list.prev != list){ return -1; } /*检查后指针*/ if(list.next != list){ return -1; } return 0; }
这应该是史上最简单的测试用例,功能很是简单,首先是故意将list结构体中的各个指针变量初始化为一个随机值。而后在调用完INIT_LIST_HEAD函数以后,检查各成员是否被初始化为了list,以判断INIT_LIST_HEAD函数是否正常工做了。注意:这个测试程序还有一个约定,返回-1表明测试失败,返回0表示测试成功。
这个测试用例是基于咱们对INIT_LIST_HEAD函数有足够的了解以后编写的,这种测试方法在软件测试领域有个正儿八经的名字,叫白盒测试。
相信经过这个NIT_LIST_HEAD函数的测试用例,你已经初步创建起了对单元测试的印象。可是千万不要觉得单元测试仅此而已,这是我刻意简化的结果。要完整地掌握单元测试,还要好好学习一段时间。
目前,对于这个小小的单元测试案例,还有不少的不足,下面简单罗列了几项:
若是对于每一次检查都采起直接写if语句的形式,将形成大量的冗余代码,而且测试用例的编写效率也会很低。
经过观察程序是否返回0或者是-1的方式来判断全部的测试是否经过并不直观,一旦出错也没法立刻判断是那一步测试出了问题。毫无疑问,咱们须要更加直观的方式来展现哪一步成功或者哪一步失败。
一份严谨的测试用例,会有大量的断定。若是一个测试程序存在100次断定,其中出现了3次失败,那最终显示一个百分比的测试经过率会比较直观,好比能够显示97%的测试成功了。
后面会进一步介绍如何本身搭建一个简单实用的单元测试框架,来解决上面这些问题。也会陆续展开介绍mock方法、打桩、错误注入、代码覆盖、动态分析、静态分析、性能优化等内容。
03
总结
正如不少其余技巧,好比打桌球、滑雪同样,测试驱动开发也要花费至关长时间来练习。许多开发者已经接受了这种技术,并且不再想回到从前“后期调试式编程”的方式去了。
它会使你的代码:
产生的bug更少
调试时间更短
彻底能够经过提交你的单元测试案例,来证实你的项目可靠性。
1.第二届国产嵌入式操做系统技术与产业发展论坛最新议程新鲜出炉!
6.重磅,传AMD 300亿美圆洽购赛灵思!最先下周达成交易
免责声明:本文系网络转载,版权归原做者全部。如涉及做品版权问题,请与咱们联系,咱们将根据您提供的版权证实材料确认版权并支付稿酬或者删除内容。