如何在单元测试中检查由工厂创建的对象图?

本文关键字:创建 工厂 对象图 检查 单元测试 | 更新日期: 2023-09-27 18:01:58

我有一些类似于下面的代码:

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方法并检查返回对象属性的测试吗?这个问题似乎表明,但这仍然适用,如果我需要检查几个层的类和属性来验证由工厂创建的对象图?这不会导致测试代码,如:

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构造拉入另一个工厂并将其作为依赖项添加到我的MyConcreteClassFactory(因此我可以从测试代码中注入它并断言created.Items的值是由我的模拟工厂创建的实例)。我预计后者将很快导致工厂-工厂和工厂-工厂-工厂-工厂的激增。毕竟,MyConcreteClassFactory的用户不应该被她必须提供特定的子工厂的事实所困扰,她应该…?

如何在单元测试中检查由工厂创建的对象图?

这当然取决于需要,但我的答案是否定的。

你永远不应该以一种"测试实现(或细节)"的方式来设计你的测试,这可能在开始的时候工作得很好,但过了一段时间你就会陷入麻烦。实现变化非常快,每一次变化都迫使你修正许多测试用例。

相反,你必须"测试行为"。它基本上意味着你抽象了所有的细节(具体的类),你的测试用例测试一些有价值的场景,而不是细节。

我的选择是创建"测试实现"用例,然后我做TDD。但是后来它们必须用"测试行为"用例重构出来。

如果您不仅考虑测试用例的数量,而且考虑构建真正的安全网的质量,这是非常重要的。