ew的小天地 科技 提高设计的可测试性的4条有效准则

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

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

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

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

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

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

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

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

  1. 避免使用static关键字以及Singleton模式

静态方法及Singleton模式会影响可测试性,因为它们把类的一些信息已经硬编码在代码中,我们很难用伪实现替换它们。

例如:

public class Database { public static Object findById(String id) {n // fetch an object from the database,n // returning a null if the id is not foundn } public static boolean objectExists(String id) { return (findById(id) != null);n }n}public class TestDatabase {n @Test public void testObjectExists() throws Exception {n // How can I fake findById() to returnn // "true" or "false" as I wish?n assertTrue(Database.objectExists("123"));n }n}

在上面的代码中,若不修改编译过的字节码是没有办法替换findById方法的,因为待测代码显式地引用了特定的实现。我们没法继承或覆盖这些方法,因为它们全都是静态的。

为了提高可测试性,建议删除Static关键字,把静态方法转换为成员方法。

  1. 隔离依赖

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

public class OrderProcessor {n public void process(Order order) {n PricingService service = getPricingService(); // 通过替换获取依赖n // use the PricingService object for processing the ordern } protected PricingService getPricingService() { /*(以下3行)覆盖返回的测试替身*/n return PricingService.getInstance();n }n}

  1. 注入依赖

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

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

public class OrderProcessor {nn private PricingService pricingService; // 用实例变量保存依赖nn /**n * Hand me my dependency by calling this method.n */n public void setPricingService(PricingService pricingService) { /*(以下3行)让其他变量给出依赖*/n this.pricingService = pricingService;n } /**n * Please call setPricingService() before invoking me. Thanks.n */n public void process(Order order) { float price = pricingService.getDiscountedPrice(order) // 直接使用依赖n }n}

在这段代码中,隔离依赖代码中的getPricingService方法变为setPricingService方法,这样就可以用成员变量保存注入的依赖。而且,process方法不再需要主动获得依赖,依赖就在实例变量中!

本文转载自,原创 软件工程之思 软件工程之思 2022-06-22 07:38 发表于福建

返回顶部