跌跌撞撞地敏捷之路——也说说测试
这个版本真正将UT落实到项目中,工具是:JUnit+EasyMock。来看看我们的UT之路:
阶段一
由于第一次使用EasyMock,看了几个例子,然后就在UT中应用它,一开始被它的几个例子误导了,下面是我们的测试方式:
1)对于一个接口定义下来后,就直接编码实现;
2)看着实现好的代码,针对每个逻辑分支,构造测试用例;
3)对于每个分支,先看看代码中该分支需要依赖哪个外部接口的哪个方法,方法需要哪些参数,然后就开始这些参数、Mock对象,并录制好Mock的预期行为,最后调用待测试接口,并对运行结果进行验收;
4)运行JUnit,看看Testcase是否执行通过,然后修改那些导致Testcase执行失败的代码,再执行用例。
这种测试方法执行了一段时间后,我们发现UT并没有发挥出人们一直吹捧的巨大威力,在我们这里好象只能起到代码检视的作用,发现的一般都是些小问题,并不能发现那些深层次的功能性问题,于是我们开始坐在一起反思。。。。。。
经过一顿海阔天空的胡撇,找到了原因:
虽然UT是白盒测试,但是我们这种方式却“白”的有点过头了,我们没有按照接口的功能去构造用例,而是在编码结束后再照着已经实现好的代码分支去构造用例,这时人的思维定势被固定在现有的实现中,失去了整体功能的把握,测试就失去了它的意义,无法发挥作用。
阶段二
在经过第一次的教训后,我们项目组内部定下了如下测试规则:
1)先确定接口的功能;
2)根据接口的功能构造所有的测试用例,每个测试用例的方法注释中写清楚该测试用例用于测试接口的场景,需要什么样的参数,接口执行的预期结果是什么;
3)编码,实现接口功能
4)补充测试用例的实现
5)执行测试用例
通过上面这种方式,真正的发挥了UT的作用,嗯,效果确实和人们吹捧的差不多,因为我们是从功能角度对代码进行测试的。
小心,不要雀跃于开发阶段的这种成就感上,后面还有个很深很深的坑等着你往里跳呢。创业难,守业更难。UT代码发挥的作用很有可能就止步于编码阶段,为什么呢?我自己发现这样一个现象:
在改问题单的时候,我改了接口中某个分支的实现,然后就拉起服务,登陆客户端进行验证,验证通过后,为了避免集成环境上的UT代码跑不通(可能是因为调用了依赖的外部接口的其它方法,或者需要修改参数等等),我会把相应的测试用例改到跑通,然后长长舒一口气,把所有代码(包括UT代码)checkin到服务器上,哈哈,打完收工。
问题出在哪里呢?代码出现问题,肯定是你的代码对于某些边界没有考虑到而导致的,也意味着你之前的UT代码没有覆盖到这个分支,OK,你现在把代码改正确了,并让以前构造的测试用例都跑通了,是件不错的事情,但你不为这个分支补充相应的用例,你如何保证后续的代码改动在这个分支上的处理是正确的呢?答案肯定是不能。在我看来,UT最最大的作用是它能保证你后续的问题修改、甚至于代码重构结果是正确的,是不会引入新问题的。
所以正确的做法是:
1)补充用例,并在用例的方法注释中写清楚该测试用例用于测试接口的场景,需要什么样的参数,接口执行的预期结果是什么;
2)修改接口实现,解决问题;
3)修改改动涉及的用例,并补充新添加用例的实现;
4)执行测试用例;
只有在所有测试用例都执行正确后,你才可以把代码放到环境中去验证。而且这样做也可以提高你的效率,因为你改完代码后就直接拿到运行环境中测试,首先得把服务器拉起,两三分钟过去了,然后再打开客户端登陆,ok又一分钟过去了,然后一测,发现没有验证通过,一看代码,靠!原来写错参数了。看是不是经常有这种现象呀^_^
说到这里,再额外提一点点。开发的还是比较喜欢编码,一个功能完成后,自己会即兴地测试几个正向用例,测试通过后,如果还有代码要写,他们就会赶紧coding下去。在开发人员coding的过程中,测试人员会针对每个功能点设计测试用例,然后将测试用例发出给开发评审,这时即使开发人员很认真、很认真的去看看这些用例,但由于他们更喜欢Coding的癖好,顶多也就是回个检视意见而已,测试用例的作用也就仅限与此了,然后到了测试的时候,开发人员拿着之前的测试用例,可能就会发现某些测试用例和实际设计不一致,或者用例缺少。代码的质量不应该过于依赖测试人员,也不应该将测试推的太靠后,可能的下面的方式效果会更好些:
1)测试人员设计用例,并将用例发给相关的开发人员;
2)开发人员编码结束后,按照测试人员设计的用例进行功能测试,如果发现测试用例有问题则直接修改用例,对于缺少的用例则进行补充,在“开发者测试”完毕后,将更新完的测试用例反馈给测试人员;
3)对于没有进行“开发者测试”的功能,测试人员不予以验收测试;
通过“开发者测试”,将测试行动推前,应该更有利于质量的保证,也不会将问题堆积到迭代的后期,增加下一个迭代的风险。我们下个版本会进行尝试。
最后,要真正提高敏捷的效率,自动化测试是不可或缺的,通过自动化测试,可以大量减少手工测试的工作量,而且也更能进一步保证版本的质量,尤其在验证代码修改不会引入新的问题上可以起到重要作用。