不少公司不少人在实践单元测试中总觉的很困难的一个很重要的缘由就是其代码自己不具备可测试性。他们每每会走入一个误区。面对一个几千行、逻辑混乱的方法而抓耳挠腮的想着用十八般武艺,各类框架去写这个方法的单元测试,而最终不得不以失败而了结,耗费了大量的时间却徒劳而无功。实际上是他们忽略了这个方法的自己是不具备可测试性的。框架
所以咱们在要对一个方法进行单测以前,必定要先看一下它是否是具备可测试性,若是不具备,那么咱们应该先对其进行重构以提升其可测试性。
在可测试的设计中,你应该很容易为代码的每一段逻辑(循环、i语句和 switchi等)快速编写一个单元测试,这些单元测试具备以下属性函数
若是编写这样的测试很困难,或者须要很长时间,那这个系统就不是一个可测试的,若是把测试看作系统的一个用户,可测试性设计就成为一种思惟方式。单元测试
若是你要测试的方法真的有几百行上千行,那么我建议先用重构一书中的方法去解决它。其次关于可测试性在实践过程当中是有一些技巧的。
2.1 方法可重写
在Java中,方法默认就是虚拟方法,可重写的。而在NET中,若是想要替换一个方法的行为,你须要明确地把方法设置为虚拟方法,才能进行重写。所以咱们能够在设计之初就尽可能把方法用关键字virtual标记。测试
若是咱们的代码都可以保持面向接口设计的原则,意味着咱们的依赖都是很容易被替换的,可让咱们在测试中年很容易取创造真实对象的伪对象。编码
密封类就是将一个类封闭起来,断其子孙的一种方式。
密封方法则不是为了防止继承而是防止重写,并且它是为了重写基类的虚方法并提供具体的实现,同时防止其后继类(派生类)再次重写该虚方法
不管是咱们的被测方法仍是其依赖的方法属于一个密封类,那么意味着这个方法是不能被重写的,在测试中也就不能去替换它。所以应该尽可能避免使用密封类设计
不少人在写代码的时候习惯于在一个方法内部初始化对象,以下面这样code
public void GetName(int userId){ //方法内部初始化类 return new User(userId).GetName(); }
这样的设计实际上是违背了代码的低耦合原则的,也不具备可测试性,所以咱们在开发中应该尽可能避免在方法中初始化另外一个对象。对于外部依赖的对象可使用依赖注入的方式。对象
要在测试中替换一个静态方法的行为,是很是困难的。
要处理这种状况,咱们可使用抽取和重写的方法进行重构,把这个静态方法抽象出去。
一个更为极端的作法是:避免使用任何的静态方法。这样的话,每一段逻辑都是一个类实例的一部分,使得这段逻辑更容易替换。有些进行单元测试或者测试驱动开发的人不喜欢使用单例,缘由之一就是单例缺乏可替换性。单例是静态的公共共享资源,很难重写它们。
要彻底避免使用静态方法可能会过于困难,可是你能够尝试在应用程序中尽可能少使用单例或者静态方法,这样在测试时会变得容易一些。继承
不管是构造函数仍是静态构造函数内的逻辑一样是咱们没法在测试中重写控制它的,所以咱们须要避免在构造函数和静态构造函数中包含逻辑代码。接口
关于可测试性设计虽然是有着许多技巧,但若是你掌握了代码编写的内容心法"SOLID原则",而且熟练应用于编码中,其实你的代码绝大多数都是具备良好的可测试性的。
可测试性当然能够更加方便的让咱们对代码进行单元测试,但有些时候也会给咱们带来一些“麻烦”。所以它也是一个很有争议的话题。有些人认为可测试性是好的设计应该具备的特征之一。也有些认为可测试性会破坏原有的设计带来一些反作用。
那么可测试性设计会带来哪些“麻烦”呢
大多数状况下,设计时以可测试性为目标会增长工做量,由于比起不考虑可测试性的设计比起可测试性设计须要编写更多的代码。
可测试性设计有时会让人以为,它把简单的事情过于复杂化了。有些接口的使用让你感受很别扭或者设计公开了你不曾考虑过的类行为语义。并且,当使用了不少接口把东西进行抽象以后,若是你要浏览基础代码找到一个方法的真正实现代码,会更加困难和麻烦。
从一些可测试性的技巧咱们能够知道有些时候为了让代码更具备可测试性,会破坏一些原有的设计原则。
正如人月神话一书中所说,在软件开发这一行业中没有银弹。一门技术或者方法论在给咱们提供了一些帮助的同时也会带来一些问题。这就须要咱们在具体使用的过程当中****case by case,根据可测试性设计所带来的优缺点找到一个平衡点!