我正在从事一个个人项目(意味着干净的源代码,没有遗留依赖项)并尝试遵循有关单元测试、依赖项管理等的最佳实践。
我公司的代码库中到处都是这样的代码:
public Response chargeCard(CreditCard card, Money amount) {
if(Config.isInUnitTests()) {
throw new IllegalStateException("Cannot make credit card API calls in unit tests!");
}
return CreditCardPOS.connect().charge(card, amount);
}
这里的目标是主动防止在测试期间执行危险代码/具有外部依赖关系的代码。如果单元测试做错事,我喜欢快速失败的概念,但我不喜欢这个实现有几个原因:
Config
它为散布在我们代码库中的静态类留下了隐藏的依赖关系。- 它改变了测试和实时行为之间的控制流,这意味着我们不一定要测试相同的代码。
- 它将外部依赖项添加到配置文件或其他一些状态保持实用程序。
- 它看起来很丑:)
可以通过更好的依赖意识来避免我们在公司代码库中使用它的相当多的地方,我正在尝试这样做,但仍有一些地方我仍在努力摆脱实现isInUnitTests()
方法。
使用上面的信用卡示例,我可以isInUnitTests()
通过将其正确包装在一个可模拟的CardCharger
类或类似的东西中来避免对每笔费用的检查,但是虽然更干净,但我觉得我只将问题提升了一个级别 - 怎么做我阻止单元测试构造一个真实的实例CardCharger
而不检查创建它的构造函数/工厂方法?
- 有
isInUnitTests()
代码味道吗? - 如果是这样,我怎样才能强制单元测试不达到外部依赖项?
- 如果不是,那么实现这种方法的最佳方法是什么,以及何时使用/避免它的良好做法是什么?
为了澄清,我试图阻止单元测试访问不可接受的资源,如数据库或网络。我完全支持像依赖注入这样的测试友好模式,但是如果一个粗心的开发人员(即我)可能违反它们,那么好的模式将毫无用处 - 在单元测试完成它们所要做的事情的情况下,快速失败对我来说似乎很重要不应该,但我不确定最好的方法。