软件开发中的形式主义--单元测试

原创
2013/09/25 12:30
阅读数 474

在我写下这个标题的时候我就知道就会被人丢板砖,但是没关系我一点都不怕,我知道挑战成见是一件吃力不讨好的事情,但我必须忠于自己的良心和思维

装逼第二弹单元测试

在许久以前,大家写完一段代码后都会在代码下面写一个main方法来调用一下刚刚写过的代码,然后检查刚刚写的代码正确与否。随着类和方法越来越多,这样的main方法也越来越多。这样会出现两个问题

  1. 测试代码和正常的逻辑代码混在一起,导致混乱

  2. 为了测试到所有的类,必须把每个类都手动执行一遍,比较费时费力

因此就有人发明的单元测试框架如大名鼎鼎的junit,有了它,我们的机械劳动就少了很多,我们的代码就清爽了很多。

-----------but  分割线----------

本来junit这种测试框架只是为了减轻我们的机械劳动,但是一些"技术大拿"们却把它用在了软件质量控制上面,他们规定代码的单元测试覆盖率必须达到某个比例,如果达不到就要批评,就认为软件质量不过关。他们的逻辑是,单元测试覆盖到的代码才能有质量保证,没有覆盖到的代码,正确性完全不可知。这样的尝试还是值得鼓励的,但是把单元测试覆盖率上升到一个严苛和非常重要的程度就有点迂腐和装逼了。

原因如下

1。单元覆盖低的代码并不一定质量低下。

测试代码的方法很多,单元测试只是其中一种而已,而且这还是只是局部测试方法而已。集成测试一样可以确保软件的正确性,而且集成测试对软件质量的保证的效率远远大于单元测试,通过的集成测试的软件基本就是一个功能上可用的软件,而经过单元测试的软件离软件可用还有很远的距离。

2。单元测试覆盖率根本不等同于代码单元的正确率

一个单元测试覆盖率很高的代码可能是非常垃圾的代码。看看下面的代码就可以看得出来。

//正常逻辑
      public Integer compute(List<Integer> nums) {
	return nums.get(0) + nums.get(1) + nums.get(2) + nums.get(3);
}


       //测试代码
      @Test
public void testCompute() {
	List<Integer> num = Arrays.asList(1, 2, 3, 4);
	assert ConsoleAction.compute(num) == 10;
}

 
 

上面的代码单元测试覆盖率是100%,但却是一段非常糟糕的代码,当list的size<4的时候,系统就会报越界错误


可以看得出来代码的质量和单元测试覆盖率是没有那么直接的关系。并且这种代码的质量和覆盖率的无关性会随技术官僚作风严重性或装逼的严重呈正比。因为当把单元测试覆盖率本身作为一个团队质量的衡量标准的时候,程序猿们就不会关心单元测试能不能测出bug,只会关心测试覆盖率能不能达到 100% ,于是最终出现的结果是团队之间比较的不是软件本身的质量,比较的只是一个覆盖率而已。

因此单元测试覆盖率只适合推广宣传鼓励,并不适合于强力执行。一旦强力执行他就会变味。


而且"技术大拿"们真的没有必要在单元测试覆盖率上死磕不放,因为真正的绝大多数的 bug都是在专门的测试阶段测试出来的,这个关把好了,软件质量不会有问题。


-------- 团队现状-----------

我经历过的好些个项目的单元测试都不是那么好,原因很明显写单元测试并不是一个轻松省力的活。测试DAO层要用专门的DBtest,还要用excel专门的构造测试数据集,测试service要用配置专门的bean的xml,远程接口还要专门的写mock。这些都是非常麻烦的事情。

更省力的办法的是,测试DAO时直接在测试数据离构造测试数据,然后触发dao,测试service的时候直接在test环境下触发并debug。虽然这种方法很土但很管用很快速。虽然这种方法在"技术大拿"们看来单元测试覆盖率为0,很不符合要求,但这其实是用另外一种方式完成了单元测试,保证的单元的正确性。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部