单元测试采用参数的抽象工厂

本文关键字:抽象 工厂 参数 单元测试 | 更新日期: 2023-09-27 18:22:13

给定一个抽象的工厂实现:

public class FooFactory : IFooFactory {
   public IFoo Create(object param1, object param2) {
      return new Foo(param1, param2);
   }
}

这个类将编写哪些单元测试?如何验证param1和param2是否被转发到Foo的创建?我必须把福的这些公共财产公开吗?这不是在破坏封装吗?或者我应该把它留给集成测试?

单元测试采用参数的抽象工厂

以下是我如何为这样一个工厂(使用xUnit.net)编写几个单元测试之一:

[Fact]
public void CreateReturnsInstanceWithCorrectParam1()
{
    var sut = new FooFactory();
    var expected = new object();
    var actual = sut.Create(expected, new object());
    var concrete = Assert.IsAssignableFrom<Foo>(actual);
    Assert.Equal(expected, concrete.Object1);
}

它会破坏封装吗?是和否…一点点。封装不仅涉及数据隐藏,更重要的是,它还涉及保护对象的不变量。

让我们假设Foo公开了这个公共的API:

public class Foo : IFoo
{
    public Foo(object param1, object param2);
    public void MethodDefinedByInterface();
    public object Object1 { get; }
}

虽然Object1属性稍微违反了Demeter定律,但它不会干扰类的不变量,因为它是只读的。

此外,Object1属性是具体Foo类的一部分,而不是IFoo接口:

public interface IFoo
{
    void MethodDefinedByInterface();
}

一旦您意识到在一个松散耦合的API中,具体成员是实现细节,这种具体的只读属性对封装的影响非常小。这样想:

公共Foo构造函数也是具体Foo类的API的一部分,所以只要通过检查公共API,我们就知道param1param2是该类的一部分。从某种意义上说,这已经"破坏了封装",因此在具体类上使每个参数都作为只读属性可用不会有太大变化。

这样的属性提供的好处是,我们现在可以对工厂返回的Foo类的结构形状进行单元测试。

这比必须重复一组行为单元测试要容易得多,我们必须假设这些测试已经涵盖了具体的Foo类。这几乎就像一个逻辑证明:

  1. 通过对具体Foo类的测试,我们知道它正确地使用了构造函数参数/与其进行了交互
  2. 从这些测试中,我们还知道构造函数参数是作为只读属性公开的
  3. 通过对FooFactory的测试,我们知道它返回了一个具体Foo类的实例
  4. 此外,我们从Create方法的测试中知道,它的参数被正确地传递给了Foo构造函数
  5. Q.E.D

好吧,假设这些参数使返回的IFoo具有真实性。测试返回实例的真实性。

您可以使FooFactory成为一个期望Foo作为参数的泛型类,并将其指定为mock。呼叫工厂。Create(…)然后创建mock的一个实例,然后您可以确认mock收到了您期望的参数。

工厂通常不进行单元测试,至少根据我的经验-对它们进行单元测试没有太大价值。因为它是接口实现映射的实现,所以它引用了具体的实现。因此,模拟Foo不可能检查它是否接收到传入的那些参数

另一方面,通常DI容器作为工厂工作,所以如果您正在使用工厂,则不需要工厂。

这取决于Foo对这两个输入对象所做的操作。检查Foo对它们所做的事情,这些事情可以很容易地被查询。例如,如果对对象调用了方法(包括getter或setter),则提供mock或stub,您可以验证是否调用了预期的东西。如果IFoo上有可以测试的东西,那么可以通过mock对象传入已知值,以便在创建后进行测试。对象本身不应该是公开的,以验证您是否通过了它们。

我还可以验证是否返回了预期的类型(Foo),这取决于测试的其他行为是否取决于IFoo实现之间的差异。

如果可能出现任何错误条件,我也会尝试强制执行这些条件,如果你为其中一个或两个对象传递null会发生什么,等等。当然,Foo将单独测试,所以你可以假设在FooFactory测试中Foo做了它应该做的事。