假设我有一个如下所示的课程:
public class Component
{
private readonly object _a;
private readonly object _b;
private readonly object _c;
public object OperationX() => new object();
public object OperationY() => new object();
public Component(object a, object b, object c)
{
_a = a;
_b = b;
_c = c;
}
}
我可以为
OperationX()
和 OperationX()
编写单元测试,这都很好。
现在,我想在另一个类中使用这个
Component
,如下所示:
public class TypeA
{
private Component _component;
public object A() => _component.OperationX();
public object B() => _component.OperationY();
public TypeA()
{
_component = new Component(new object(), new object(), new object());
}
}
该类本质上将
A()
和 B()
的工作转发给 Component
类。虽然它只是转发工作,但我仍然想编写测试以确保A()
和B()
的结果是预期的结果。例如,A()
和B()
可能是虚拟的,我可能不小心忘记覆盖它们,或者它们可能在合并过程中被破坏。因此,对它们进行测试很重要。
如果我要为
A()
和 B()
编写测试,我基本上会重复我为 Component
编写的测试?
我发现自己在测试中做了很多重复,因为我经常使用构图,但我不确定它是好是坏?
如果我创建另一个类将方法转发到
TypeA
的实例会怎么样?现在我将第三次重复相同的测试。
我想我可以让
Component
有一个接口并注入到 TypeA
中,这样我就可以模拟它,并测试 OperationX()
是否被调用,而不是实际测试结果。但对我来说这有缺点:
Component
的每个创建都需要传入Component
的新实例。所以现在为了测试,我把调用代码弄得更混乱了。Component
是一个非常小的对象,并且包含在依赖注入中似乎有些过分了我认为您应该尽可能将您的被测系统视为黑匣子。
事实上
TypeA
依赖于 Component
来执行其工作,这是一个 实现细节,不应泄漏到您的测试中。这在未来可能会改变,例如您可能决定摆脱 Component
并将其代码集成到 TypeA
中,或者您可能决定将 Component
分成两个不同的类。
因此,从这个角度来看,我想说以某种方式重复您为
Component
和 TypeA
测试执行的断言并没有不正确。
但是,如果您发现自己一次又一次地重复这种重复,我想说您的课程可能存在设计问题。您说您正在使用组合,但是“内部类型”提供的功能不应该与“外部类型”提供的功能相同(组成“内部类型”的那些)。
您的“外部类型”可能会简单地转发到内部类型,例如,如果它充当Facade,但这应该很少见。