软件开发中的决策:权衡与取舍
上QQ阅读APP看书,第一时间看更新

1.1.1 单元测试

编写测试时,你需要决定测试哪部分代码。譬如,你需要对一个简单的组件SystemComponent进行单元测试,它只提供了一个声明为public类型的接口,其他所有方法的声明都是private类型的,客户端无法直接访问。该场景的代码片段如代码清单1.1所示。

代码清单1.1 组件单元测试

public class SystemComponent {
 
  public int publicApiMethod() {
    return privateApiMethod();
  }
 
  private int privateApiMethod() {
    return complexCalculations();
  }
 
  private int complexCalculations(){
    // 复杂的代码逻辑
    return 0;
  }
}

这时你需要判断,要不要为complexCalculations()添加单元测试,是否继续保持该方法的私有成员属性。这类单元测试属于黑盒测试,只能覆盖public类型的API。通常,单元测试做到这种程度就已经足够了。然而,极端的情况下,譬如私有方法的逻辑特别复杂时,为其添加单元测试也是物有所值的。为了做到这一点,你得放开complexCalculations()的访问权限。代码清单1.2展示了对应的修改。

代码清单1.2 通过公有访问方式进行单元测试

@VisibleForTesting
public int complexCalculations() {
  // 复杂的代码逻辑
  return 0;
}

修改方法的可见性,将其由private类型变为public类型之后,你可以为这部分之前访问级别为private的API编写单元测试。由于公有方法对所有API客户端都可见,你不得不面对客户端可以直接调用该方法的窘境。你可能会说,上述代码清单中不是还有@VisibleForTesting注解吗?实际情况是,这个注解只能起到“提示信息”的作用,无法强制限定调用方不使用你的API中的公有方法。如果调用方没有留意这个注解,他们可能会忽略这一点。

本节提到的两种单元测试方法并无高低优劣之分。后一种方法提供了更高的灵活性,然而,随之而来的是维护代价的增加。你可以在这二者间取一个折中方案。譬如,将代码的包标记为private类型。这样一来,由于测试代码与产品代码在同一个包内,你可以直接在测试代码中调用上述方法,而不再需要将方法修改成public类型的。