分享

提高设计的可测试性的4条有效准则

 东北十三少 2022-06-22 发布于四川

测试驱动开发能够提高代码的可测试性,但提高代码的可测试性却不能只靠测试驱动开发。事实上,有些设计会提高可测试性,有些设计可能会影响可测试性。测试要使设计向提高可测试性方面驱动,而非相反。

例如,当我们知道Singleton模式通常会影响可测试性,那么在写测试时就可以避免把代码向Singleton模式上驱动。

提高可测试性的设计准则,有以下四条有效准则:

  1. 尽量使用组合而非继承

从多个独立的功能构建出复杂的对象,在面向对象语言中,继承和组合都可以用来实现此类功能。

但是继承在设计的可测试性、可维护性以及复杂性方面都有负面影响。例如在Java中,实例化子类必须要提供只有其父类构造函数才需要的各项参数。如果这些参数本身又是很复杂的对象,需要费很多功夫才能初始化。另外,哪怕是极小的修改所产生的影响,都可能在整个继承体系内产生较大的影响。这会使测试变得很麻烦。

而使用组合则能够更灵活的重用类的功能。一个组合中,顶层的组合对象会把工作委托给其各个组成部分,而不是通过调用父类的方法来完成工作。所以,虽然组合比继承稍显复杂,代码量更多,但是组合能够提高可测试性、适应性以及可维护性。

  1. 避免使用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关键字,把静态方法转换为成员方法。

  1. 隔离依赖

在把静态方法的访问移至成员方法后,我们可以很方便地用测试替身替换依赖,提高可测试性。隔离依赖的示例代码如下:

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();
}
}
  1. 注入依赖

通过让外部环境注入依赖,而不是在代码中自己查找依赖,可以更有效地提高可测试性。

依赖注入的示例代码如下:

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,译者:李贝,出版社:人民邮电出版社

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多