单元测试的一些感想

最近终于说服了项目经理,在咱们项目中采用单元测试。愿意是之前遇到了很大问题,由于我们项目中要用到很多其他系统的webservice接口,经常要等这些接口过来了我们才能测试,这样开发人员就很容易忘记哪些地方是接口还不通的,因而遗漏了某个接口。甚至更有的开发人员,自己都不测试自己代码, 到了功能验收期间被QA提及bug,搞得整个team加班。
于是我提倡赶紧加入单元测试, 接口不通的地方肯定会fail掉,而且也会强迫开发人员自己测试自己的代码,减少功能上的bug,减少加班。项目经理这下感兴趣了,让我赶紧整理方案。

我很清楚我们团队的情况,只适合在business层的接口上做大粒度的单元测试,搭建测试环境,写测试代码也是简单的,我还是花了一天时间考虑怎样把文章写好一点,也算是让自己对单元测试有个相对全面的了解。

我之前是做过business和dao测试的, 还包括我写的公用方法,但都是针对功能的。我比较想知道大家做单元测试倾向于功能还是纯代码。

在看过第五部分提到的相关讨论之后,我写下了以下文字,也做是一个总结。

一、 单元测试的目的和优点
1)首先保证代码质量。
2)其次保证代码的可维护。
3)再此保证代码的可扩展。

其实我们更关心的应该是1),第一时间能够测试代码是否通过肯定是我们最关心的。然而很多程序员和老板都会怀疑,程序代码+测试代码 明显会在加重工作量,影响项目进度。

事实上, 缺少测试的代码,就很有可能是整个项目中的一个个地雷, 随时都有引爆的隐患,而且查找它们很困难。Bug越是发现的晚就越难解决,因此缺少测试的代码到了后期就会让开发人员变得焦虑不安,精神紧张,开始烦躁解决bug,害怕新的需求,因为他们发现程序变得如此脆弱,想要重构时却面对无测试代码保障的代码,变得没有信心。

很多团队试用单元测试的经历表明,合理的单元测试不但不会增加工作量和成本,反而降低了它们,提高了效率。我想这也是为什么我们可以推行单元测试的原因,至少看到了未来是美好的。

2)和3),也有必要谈一下。其实在有利保证了1)的情况下, 2)和3)就是顺理成章了,测试会让程序员努力写出松耦合的代码,因此可维护和可扩展就自然收获了, 可谓一举三得。

声明了以上三个目的之后,优点也明显看出来了:除了有验证保证, 还通过编写可测试代码来提高维护和伸缩性。

二、 单元测试的粒度
明确了要进行单元测试之后, 我们关心的应该是单元测试面向何方?难道项目里每个类、方法,我们都去测试吗?
我觉得理想情况下确实也是可以的每处都测试的,但又没有必要。解答这个问题, 首先要问我们做单元测试的出发点和动机。我们关心的可能是业务接口功能是否满足, 于是就在业务接口上进行测试;我们担心公用类会有问题, 于是给公用类加上单元测试。 只在最需要测试的地方进行测试,应该是最好的实践。满足以下两条,我觉得都可以加测试了:
a) 提供业务功能
b) 复用高的模块
c) 自己想用单元测试给自己信心
好了,以上分析在哪里做测试, 已经是粒度的问题了。还可以再细分。
下面举个例子来看,假定我们有一个UserService, 我们现在要测试其中的registerUser方法,希望能看到一个正常的用户注册过程,这就是一个usercase(用例)。那么看看registerUser里面会做什么, 肯定会先findUser看看是否有同名的用户存在, 如果不存在就addUser。 这样,registerUser就被分成了两部:findUser和addUser, 如果再细分, 会发现可能还调用了UserDao,调用了里面的findUser和addUser。
这样就发现问题了,我们只要测试一个usercase,可以将整个registerUser视为一个测试单元,但这个单元又可以划分成好几个小的单元,那我们的单元测试是否也要在小单元上做?
还是取决于目的, 如果只关心usercase,那么就把registerUser当成一个黑盒, 不管里面怎么实现的,我只关心往黑盒输入得到我预期的输出即可, 功能满足了就收工。
当然细粒度的测试是受欢迎的,如果能先把里面几个小的单元测试通过了,再测试外面的这个大的,不是更放心吗。事实上单元测试是鼓励细粒度测试的,那样更能保证代码的松耦合,避免过长的代码和过多的分支,这也是OO的基本思想, 也是我们愿意看到的。

三、 如何做单元测试
单元测试,说起来很容易做,以一个方法为例。就是输入什么参数, 实际返回什么结果, 预期什么结果, 他们是否一致?是否一直都是一致?
这时,这个方法就成了一个黑盒,根据api契约,我们创建各个测试用例,努力测试功能是否通过。
你会发现,单元测试能够帮我们充分了解代码的用法,从效果上而言,单元测试就像是能执行的文档,说明了在你用各种条件调用代码时,你所能期望这段代码完成的功能。
OK,单元测试只是在帮助你了解代码的意图, 因此你有什么意图就怎么测试好了。如果不是你写的代码,别人要了解你的代码的用意,除了看api doc,这个测试用例不是也能帮上忙吗?是啊, 太美妙了。

但有时候我们并不会只将方法视为黑盒, 比如我们需要做边界测试,比如方法的一个参数是数组,传入null会怎样?[]会怎样?本来数组的长度应该是3,如果我传长度为2的数组进去会怎样?这些都是必须做白盒测试才能看到的,这时就不再是功能测试,而是基于代码的测试。类似的还有覆盖率测试, 必须打开黑盒,分析里面的分支、条件、路径才行。错误测试,什么条件会导致Error,或者异常呢?可以看出,单元测试如果要做白盒的就复杂很多。

我们需要黑盒or白盒的单元测试?还是取决测试的目的。

四、 适合我们的有效单元测试
综合以上谈到的三点,怎样制定适合自己团队的有效的单元测试方案?
只要从实际情况出发就简单了,。好比我们的项目就只适合在business和common上进行单元测试,因为我们更加关心business提供的服务是否能通过, 而common是公用类,大面积使用当然也需要确定它们的质量。Cmmon测试的粒度可以考虑再细分,因为common主要出自一人之手,重构起来方便。而business测试不适合对原有代码再进行更细粒度测试,因为开发人员水平普遍不高,重构相对困难、时间也紧迫。

五、参考资料
http://www.iteye.com/topic/14021
http://www.iteye.com/topic/22273
http://www.iteye.com/topic/18033
http://www.iteye.com/topic/97693

六、推荐学习的书籍
《junit in action》,《JUnit Recipes》,另外就是spring、appfuse源码了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值