楔子

四年前我虽然也写了很多年代码,由于公司虽然规模不小,却并非一家规范化的软件公司,因此在项目中严格意义上来说并没有架构设计、也不写单元测试,后来有幸加入了一家公司,这家公司虽然也是一家小公司,但是好歹曾经聘请过一位架构师,这位架构师使用spring.net 搭建了一套基础的技术架构,并在公司推广使用EnterpriseArchiture(EA)软件设计UML图,但是由于种种原因,他试用期还没过,只是简单的设计了几个业务之后就离职了。

我加入这家公司的时候距离这位架构师离职也已经有相当长的时间,未能直接学习的机会,但是非常还能接触到这位优秀架构师留下来的部分代码和资料,这为我的职场之路打开了新的窗户,从这些资料中学到了许多之前一直想学但是没机会学的内容,并第一次接触到单元测试这种技术。

然而,值得讽刺的是,在他搭建的项目架构中,单元测试代码也只有一个简单的示例,并没有针对业务功能编写任何的具体测试代码,而实际上经手这个项目的开发者已经超过了几十个人,其实这也从一个侧面说明中国相当一部分.Net 开发者跟我一样,几乎都不写单元测试。甚至是不少开源代码贡献者,他们贡献的开源组件,也并没有设计单元测试的代码。但是再来看国外的一些优秀的开源组件,往往也都会提供单元测试的代码,TDD实际上正是一种非常便捷高效门槛低的设计模式。

后来,我经手的每一个项目都会按照TDD的方式写单元测试,但是依然没有深刻领悟单元测试的精髓,只是把它当做代码调试的一个入口而已。其实这种单元测试依然不合格,尤其是认真的运行一下代码覆盖率报告之后,就会发现,单元测试的代码覆盖率只有10个点,与微软的官方标准单元测试覆盖率100%相比,简直就是不合格。

什么是单元测试?

一直以来大家有一种错误的理念,总是认为单元测试作为一种测试方法,主要是由测试工程师来主导的一种测试手段。并非如此,借用教科书上的说法,单元测试作为一种最基础的测试手段,其主要作用是“在计算机编程中,针对程序模块(最软件设计中的最小单元)进行正确性检验的测试工作。”

之前曾经阅读过一篇文章,作者提了一个例子,他说他儿子在使用乐高积木做玩偶过程中,组装完一些零部件之后,就会试图去测试一下部件是否能够正常运转,作者说这种过程实际上就是一种单元测试。他认为,单元测试的思想其实普遍存在于人类文明的发展过程中,从人类开始制作工具来说,就需要使用单元测试的方法对工具的可用性进行测试。尤其是进入工业时代以来,传统制造业尤其重视产品质量,为了提高产品质量,采取了一系列措施,这种例子其实不胜枚举。

去年我曾经有幸为一家国企开发过一些质量管理系统,并深入了解了这些企业的质量管理流程,虽然从宏观层面来看,以互联网经济为代表的新兴经济对以包括制造业在内的实体经济带来了许多冲击,但是依然那些优秀的制造业企业依然坚定的将产品质量作为核心竞争力,并通过互联网的这种模式,让企业的发展获得了新的机遇。

软件行业其实本质上和制造业没有那么巨大的区别,以软件开发过程中,由软件工程师们开发出来的每一个方法实际上就是开发者们解决虚拟世界生存问题的零部件,每一个产品或应用就是这样的工具。在一个软件开发过程中,往往需要涉及到多人操作,经常需要调用别人编写的模块代码。尤其是在面向互联网开发中,如果不能认真的对待每一行代码,就有可能一些疏忽大意造成不可弥补的后果。

因此做好单元测试,不仅仅只是开发者们应该采用的一种测试手段,而是应该是一种基本的技术手段,对我们开发的每一行代码,都应该使用单元测试进行覆盖,自己负责的模块定义应该尽量明确,模块内部的更改不会影响他模块,让模块的质量得到稳定和良好的保证。

为什么大家认为单元测试不合时宜?

当然,大部分开发者也许明白这个道理,但是却认为单元测试不合时宜,主要包括以下几点:

1,没有时间 
---- 在软件开发过程中,往往以完成任务为第一要务,而由于时间计划的安排,单元测试的设计和实践均需要花费大量的时间完成,过度的使用单元测试会拖延项目的进度,而且花费的边界成本高昂而收效甚微。 
---- 笔者认为,跟随代码一起,设计和应用单元测试,并不会显著的带来时间上的损害,反而会让开发者更加关注代码本身的目的,让输入输出和计算过程更为合理。

2,计划会变化:需求变化快,单元测试跟不上需求的变化 
由于软件产品需求的不确定性,导致之前设计的单元测试并非符合项目的实际功能性需求,没必要花费这个精力设计这种没用的场景。 
----笔者认为,需求变化与是否应用单元测试关系不大。

3,单元测试是用来找Bug的,应该有测试来编写 
单元测试作为一种测试手段,是为了发现代码中存在的bug。 
--- 笔者认为:单元测试的目的是为了让程序作者更加快捷的发现代码中存在的问题,并在第一时间发现和解决,从而保障软件质量。

4,有程序员定义的单元测试没有意义,因为程序员设定的逻辑场景就有可能不准确 
单元测试作为由程序员根据实际用例出发设计的测试流程,本身可能并非符合业务的实际应用需要,因此单元测试应该由最了解需求的人来进行设计。而且完成单元测试设计后,也需要有了解需求的人对单元测试用例和代码进行审查。 
--- 笔者认为:由程序员开发的单元测试没有意义,那么他写的代码呢?写代码本身就是为了完成指定的用例场景,如果单元测试都不合理,那么如何确保代码本身就合理?

5、TDD测试驱动,不过是一种形式主义,本身就不合时宜 
在TDD模式中,测试先于开发,所以需要经过良好的设计和定义,最好能够解耦合各个模块,让代码更加完美的匹配测试代码。但是这种代码本身就要求经验丰富的开发者才能完成,而能够完成这种能力的,本身就属于优秀开发者,并不需要设计单元测试代码。 
-- 笔者认为:优秀的开发者,本身不仅仅只是因为他们出身优秀,而是因为他们掌握了包括单元测试等在内的优秀学习方法并每天都在实践。

好的单元测试的标准

笔者最近阅读《构建之美》(第一版)深刻认同提出的关于好的单元测试的标准,例如以下几点: 
1、单元测试应该在最低的功能\参数上验证程序的正确性。 
2、单元测试应该由最熟悉代码的人(程序的作者)来写。 
3、单元测试过后,机器状态保持不变。---笔者认为,不仅仅机器的状态应该保持不变,数据中存储的数据关系也应该保持不变,例如,通过单元测试录入的数据,应该在完成测试后自动回滚,或者添加测试戳记后,手动清除。 
4、单元测试要快(一个测试的运行时间是几秒钟,而不是几分钟)。 
5、单元测试应该产生可重复、一致的结果。 
6、单元测试具有独立性的特点,单元测试的运行、通过、失败不依赖于别的测试,可以认为构造数据,以保持单元测试的独立性。 
7、单元测试应该负载所有的代码路径。 
8、单元测试应该继承到自动测试的框架中。 
9、单元测试必须与产品代码一起保存和维护。

何妨不试一试呢?

在软件企业的发展前期,往往会由于技术上的一些偶发性突破,取得短期的优势,但是随着企业的规模发展,使用更加规范化甚至成为形式化的方式确保产品的质量是必然趋势,而单元测试则是一种看似简单,但却效果显著的手段,相信通过一段时间的实践,就会深刻的领悟到其中的妙处。

由于时间仓促,可能没什么干货,下一篇再进一步讨论单元测试、代码覆盖率的问题。

为什么从前那些.NET开发者都不写单元测试呢?的更多相关文章

  1. 【快学springboot】在springboot中写单元测试[Happyjava]

    前言 很多公司都有写单元测试的硬性要求,在提交代码的时候,如果单测通不过或者说单元测试各种覆盖率不达标,会被拒绝合并代码.写单元测试,也是保证代码质量的一种方式. junit单元测试 相信绝大多数的J ...

  2. 【快学springboot】在springboot中写单元测试

    前言 很多公司都有写单元测试的硬性要求,在提交代码的时候,如果单测通不过或者说单元测试各种覆盖率不达标,会被拒绝合并代码.写单元测试,也是保证代码质量的一种方式. junit单元测试 相信绝大多数的J ...

  3. C++开发者都应该使用的10个C++11特性

    转载自http://blog.jobbole.com/44015/ 在C++11新标准中,语言本身和标准库都增加了很多新内容,本文只涉及了一些皮毛.不过我相信这些新特性当中有一些,应该成为所有C++开 ...

  4. 每一个web开发者都应该了解的HTTP/2

    我认为每一个 web 开发者都应该对这个支撑了整个 Web 世界的 HTTP 协议有所了解,这样才能帮助你更好的完成开发任务.在这篇文章中,我将讨论什么是 HTTP,它是怎么产生的,它的地位,以及我们 ...

  5. 转载:每个C++开发者都应该使用的十个C++11特性

    这篇文章讨论了一系列所有开发者都应该学习和使用的C++11特性,在新的C++标准中,语言和标准库都加入了很多新属性,这篇文章只会介绍一些皮毛,然而,我相信有一些特征用法应该会成为C++开发者的日常用法 ...

  6. 每个Javascript开发者都应当知道的那些事

    每个Javascript开发者都应当知道的那些事 2015-06-07 前端大全 (点击上方蓝字,可快速关注我们) Javascript是一种日益增长的语言,特别是现在ECMAScript规范按照每年 ...

  7. C++开发者都应该使用的10个C++11特性 转

    http://blog.jobbole.com/44015/// | 分类: C/C++, 开发 | 条评论 | 标签: C++, C语言 分享到: 本文由 伯乐在线 - 治不好你我就不是兽医 翻译自 ...

  8. 开发者都应该使用的10个C++11特性

    摘要: 在C++11新标准中,语言本身和标准库都增加了很多新内容,本文只涉及了一些皮毛.不过我相信这些新特性当中有一些,应该成为所有C++开发者的常规装备.你也许看到过许多类似介绍各种C++11特性的 ...

  9. 人工智能都能写Java了!这款插件让你编程更轻松

    最近在浏览技术社区,发现了一款 IDE 插件,利用人工智能技术帮助程序员高效写代码.节省开发时间,一下子勾起了我的好奇心. 下载之后,使用一番,确实蛮好的,可以有效提升编程效率. 这款插件叫:aixc ...

随机推荐

  1. WinForm中DataGridView对XML文件的读取

    转自http://www.cnblogs.com/a1656344531/archive/2012/11/28/2792863.html c#读取XML   XML文件是一种常用的文件格式,例如Win ...

  2. JavaScript的数据类型问题总结

    一    JS中有哪些数据类型? -- 共 6 种数据类型,其中 5 种基本数据类型Undefined,Null,Boolean,Number和 String,1 种复杂数据类型Object.因为EC ...

  3. Java多线程简介

    Java多线程简介 Java中内置了对多线程的支持,让多线程的开发方便很多,但同时也带来了另外的复杂,线程间的交互以及很多的不确定性让多线程又显得很复杂.在此只是针对Java中多线程的基础做些说明,有 ...

  4. C++中函数重载和函数覆盖的区别

    C++中经常会用到函数的重载和覆盖,二者也在很多场合都拿出来进行比较,这里我就对二者的区别做点总结: 函数重载: 函数重载指的是函数名相同.函数特征值不同的一些函数,这里函数的特征值指的是函数的参数的 ...

  5. BZOJ3613: [Heoi2014]南园满地堆轻絮

    分析: 构造数据时间有些长,可以用秦九韶优化一下. 二分答案+贪心,即:另每一个b[i]尽可能的小的同时满足题意,在枚举过程中,判断是否存在一个b[i-1]>a[i]+x 如果存在,那么向右找 ...

  6. ArcGIS API for JavaScript 入门教程[1] 渊源

    ->对于萌新,你可能需要了解一下这个东西是什么 ->对于已经知道要用这个东西的开发者,你可能需要了解一下它的底层机制 不针对大牛.龟速更新ing. 转载注明出处.博客园&CSDN& ...

  7. 【爆料】-《阿伯泰大学毕业证书》Abertay一模一样原件

    ☞阿伯泰大学毕业证书[微/Q:865121257◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归&a ...

  8. 【Canal源码分析】Sink及Store工作过程

    一.序列图 二.源码分析 2.1 Sink Sink阶段所做的事情,就是根据一定的规则,对binlog数据进行一定的过滤.我们之前跟踪过parser过程的代码,发现在parser完成后,会把数据放到一 ...

  9. Java注解(二):实战 - 直接使用对象列表生成报表

    通过对Java注解(一):介绍,思想及优点学习了解,相信大家对Java注解有一定程度的了解,本篇文章将实战项目中的应用来加深对Java注解的了解. 本实例实现根据指定字段的JavaBean,生成对应列 ...

  10. ASP.NET Core 实现带认证功能的Web代理服务器

    引言 最近在公司开发了一个项目,项目部署架构图如下: 思路 如图中文本所述,公司大数据集群不允许直接访问外网,需要一个网关服务器代理请求,本处服务器A就是边缘代理服务器的作用. 通常技术人员最快捷的思 ...