测试驱动开发能够提高代码的可测试性,但提高代码的可测试性却不能只靠测试驱动开发。事实上,有些设计会提高可测试性,有些设计可能会影响可测试性。测试要使设计向提高可测试性方面驱动,而非相反。 例如,当我们知道Singleton模式通常会影响可测试性,那么在写测试时就可以避免把代码向Singleton模式上驱动。 提高可测试性的设计准则,有以下四条有效准则: 尽量使用组合而非继承
从多个独立的功能构建出复杂的对象,在面向对象语言中,继承和组合都可以用来实现此类功能。 但是继承在设计的可测试性、可维护性以及复杂性方面都有负面影响。例如在Java中,实例化子类必须要提供只有其父类构造函数才需要的各项参数。如果这些参数本身又是很复杂的对象,需要费很多功夫才能初始化。另外,哪怕是极小的修改所产生的影响,都可能在整个继承体系内产生较大的影响。这会使测试变得很麻烦。 而使用组合则能够更灵活的重用类的功能。一个组合中,顶层的组合对象会把工作委托给其各个组成部分,而不是通过调用父类的方法来完成工作。所以,虽然组合比继承稍显复杂,代码量更多,但是组合能够提高可测试性、适应性以及可维护性。 避免使用static关键字以及Singleton模式
静态方法及Singleton模式会影响可测试性,因为它们把类的一些信息已经硬编码在代码中,我们很难用伪实现替换它们。 例如: public class Database { public static Object findById(String id) { // fetch an object from the database, // returning a null if the id is not found } public static boolean objectExists(String id) { return (findById(id) != null); } }public class TestDatabase { @Test public void testObjectExists() throws Exception { // How can I fake findById() to return // "true" or "false" as I wish? assertTrue(Database.objectExists("123")); } }
在上面的代码中,若不修改编译过的字节码是没有办法替换findById方法的,因为待测代码显式地引用了特定的实现。我们没法继承或覆盖这些方法,因为它们全都是静态的。 为了提高可测试性,建议删除Static关键字,把静态方法转换为成员方法。
隔离依赖
在把静态方法的访问移至成员方法后,我们可以很方便地用测试替身替换依赖,提高可测试性。隔离依赖的示例代码如下: public class OrderProcessor { public void process(Order order) { PricingService service = getPricingService(); // 通过替换获取依赖 // use the PricingService object for processing the order } protected PricingService getPricingService() { /*(以下3行)覆盖返回的测试替身*/ return PricingService.getInstance(); } }
注入依赖
通过让外部环境注入依赖,而不是在代码中自己查找依赖,可以更有效地提高可测试性。 依赖注入的示例代码如下: public class OrderProcessor {
private PricingService pricingService; // 用实例变量保存依赖
/** * Hand me my dependency by calling this method. */ public void setPricingService(PricingService pricingService) { /*(以下3行)让其他变量给出依赖*/ this.pricingService = pricingService; } /** * Please call setPricingService() before invoking me. Thanks. */ public void process(Order order) { float price = pricingService.getDiscountedPrice(order) // 直接使用依赖 } }
在这段代码中,隔离依赖代码中的getPricingService方法变为setPricingService方法,这样就可以用成员变量保存注入的依赖。而且,process方法不再需要主动获得依赖,依赖就在实例变量中! 这正是: 提高设计测试性,四条原则可期待 避免静态和单例,善用组合与依赖 参考书目:测试驱动开发的艺术,作者:Lasse Koskela,译者:李贝,出版社:人民邮电出版社
|