我一直在尝试找出编写测试友好代码的最佳实践,但更具体地说,是与对象构造相关的实践。在蓝皮书中,我们发现我们应该在创建对象时强制执行不变量以避免我们的实体、值对象等的损坏。考虑到这一点,按合同设计似乎是避免我们的对象损坏的解决方案,但是当我们遵循这一点,我们最终可能会编写如下代码:
class Car
{
//Constructor
public Car(Door door, Engine engine, Wheel wheel)
{
Contract.Requires(door).IsNotNull("Door is required");
Contract.Requires(engine).IsNotNull("Engine is required");
Contract.Requires(wheel).IsNotNull("Wheel is required");
....
}
...
public void StartEngine()
{
this.engine.Start();
}
}
嗯,这乍一看还不错吧?似乎我们正在构建一个安全类来公开所需的合同,因此每次Car
创建对象时,我们都可以确定该对象是“有效的”。
现在让我们从测试驱动的角度来看这个例子。
我想构建对测试友好的代码,但为了能够单独测试我的Car
对象,我需要为每个依赖项创建一个模拟存根或一个虚拟对象来创建我的对象,即使我可能只是想测试一种仅使用这些依赖项之一的方法,例如StartEngine
方法。遵循 Misko Hevery 的测试哲学,我想写我的测试,明确指出我不关心 Door 或 Wheel 对象只是将 null 引用传递给构造函数,但由于我正在检查 null,所以我不能这样做
这只是一小段代码,但是当您面对真正的应用程序时,编写测试变得越来越难,因为您必须解决主题的依赖关系
Misko 建议我们不应该在代码中滥用空值检查(这与契约式设计相矛盾),因为这样做,编写测试变得很痛苦,作为替代方案,他说最好编写更多的测试,而不是“有这样的错觉”我们的代码是安全的,因为我们到处都有空检查”
您对此有何看法?你会怎么做?最佳实践应该是什么?