我有一些类似于下面的代码:
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 实例的事实——而不是具体实现是什么。尽管如此,我还是想测试一下工厂类是否做了它应该做的事情——构建一个具体的对象图。
我是否应该编写一些测试来调用创建方法并检查返回对象的属性? This question似乎表明是这样,但是如果我需要检查多层类和属性以验证工厂创建的对象图,这是否仍然适用?这不会导致测试代码如:
var created = objectUnderTest.Create(MyEnum.A);
var itemBuilder = (created.Items as MyLazyItemCollection).Builder;
Assert.IsInstanceOfType(itemBuilder, typeof(MyRemoteItemBuilder));
我不是特别喜欢 created.Items
的沮丧,但在我看来,这是断言工厂创建了 MyLazyItemCollection
的唯一方法正确,因为并非每个 IMyItemCollection
都可以预期具有 builder 属性......这只是图表的第二层。我可能需要进一步深入了解 MyRemoteItemBuilder
的依赖关系,看看它们是否已正确创建:
var service = ((created.Items as MyLazyItemCollection)
.Builder as MyRemoteItemBuilder).Service;
Assert.IsInstanceOfType(service, typeof(MyService));
我应该以这种方式测试我的工厂,接受难看的嵌套向下转型 - 毕竟这是测试代码 - 或者我应该将 IMyItemCollection
构造拉到另一个工厂并将其作为依赖项添加到我的 MyConcreteClassFactory
(所以我可以从测试代码中注入(inject)它并断言 created.Items
的值是由我的模拟工厂创建的实例)。我预计后者会很快导致工厂-工厂和工厂-工厂-工厂的爆炸式增长。毕竟,MyConcreteClassFactory
的用户不需要为她必须提供特定子工厂这一事实而烦恼,她应该……吗?
最佳答案
当然要看需求,但我的回答是否定的。
你永远不应该以“测试实现(或细节)”的方式设计你的测试,这在开始时可能工作得很好,但过了一段时间你就会遇到麻烦。实现变化非常快,每次更改都必须纠正许多测试用例。
相反,您必须“测试行为”。它基本上意味着您抽象了所有细节(具体类),并且您测试用例来测试一些有值(value)的场景,而不是细节。
我的选择是创建“测试实现”用例,然后再进行 TDD。但后来他们必须用“测试行为”案例进行重构。
如果您不仅要论证测试用例的数量,还要论证构建真实安全网的质量,这一点非常重要。
关于c# - 我应该如何检查工厂在单元测试中创建的对象图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6689884/