我有一些类似于以下内容的代码:
public interface IMyClass
{
MyEnum Value { get; }
IMyItemCollection Items { get; }
}
public class MyConcreteClassFactory : MyClassFactoryBase
{
public override IMyClass Create(MyEnum value)
{
var itemBuilder = new MyRemoteItemBuilder();
var itemCollection = new MyLazyItemCollection(itemBuilder)
return new MyClass(value, itemCollection);
}
}
'真正的'代码应该只关心工厂返回IMyClass实例的事实 - 而不是具体实现。不过,我想测试一下工厂类是做了它应该做的 - 构建一个具体的对象图。
我应该编写一些调用create方法并检查返回对象属性的测试吗? This question似乎表明了这一点,但如果我需要检查几层类和属性来验证工厂创建的对象图,那么它仍然适用吗?这不会导致测试代码,如:
var created = objectUnderTest.Create(MyEnum.A);
var itemBuilder = (created.Items as MyLazyItemCollection).Builder;
Assert.IsInstanceOfType(itemBuilder, typeof(MyRemoteItemBuilder));
我并不是特别喜欢created.Items
的垂头丧气,但我认为这是断言工厂正确创建MyLazyItemCollection
的唯一方法,因为不是每一个IMyItemCollection
可以预期有一个构建器属性......而这只是图的第二层。我可能需要进一步深入研究MyRemoteItemBuilder
的依赖关系,看看它们是否被正确创建:
var service = ((created.Items as MyLazyItemCollection)
.Builder as MyRemoteItemBuilder).Service;
Assert.IsInstanceOfType(service, typeof(MyService));
我应该以这种方式测试我的工厂,接受难看的嵌套向下转换 - 毕竟这是测试代码 - 或者我应该将IMyItemCollection
构造拉到另一个工厂并将其作为依赖项添加到我的{{ 1}}(所以我可以从测试代码中注入它并断言MyConcreteClassFactory
的值是我的模拟工厂创建的实例)。我预计后者会很快导致工厂和工厂工厂爆炸。毕竟,created.Items
的用户不应该为她必须提供特定的子工厂而烦恼,如果她......?
答案 0 :(得分:1)
这取决于需要,但我的答案是否定的。
你绝不应该以“测试实现(或细节)”的方式设计你的测试,这可能在开始时非常好用,但过了一段时间你就会遇到麻烦。实现非常快,每次更改都会被迫纠正许多测试用例。
相反,你必须“测试行为”。它基本上意味着您抽象所有细节(具体类),并测试测试一些有价值的场景而不是细节的案例。
我的选择是创建“测试实现”案例,然后我就是TDD。但是后来他们必须用“测试行为”案例进行重构。
如果您不仅要考虑测试案例的数量,而且要建立真正的安全网的质量,这一点非常重要。