8

给定一个抽象工厂实现:

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

将为这个类编写哪些单元测试?如何验证 param1 和 param2 是否已转发到 Foo 的创建?我必须制作 Foo 的这些公共属性吗?那不会破坏封装吗?还是我应该把它留给集成测试?

4

5 回答 5

11

以下是我如何为这样的工厂(使用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. 量子点
于 2012-02-03T07:35:20.633 回答
2

好吧,大概这些参数使返回的IFoo内容具有真实性。测试返回的实例是否属实。

于 2012-02-02T23:37:30.533 回答
0

您可以使 FooFactory 成为一个期望 Foo 作为参数的泛型类,并将其指定为模拟。调用 factory.Create(...) 然后创建一个模拟实例,然后您可以确认模拟接收到您期望的参数。

于 2012-02-02T23:25:19.910 回答
0

这取决于 Foo 对 2 个输入对象的作用。检查 Foo 对它们所做的可以轻松查询的操作。例如,如果在对象上调用方法(包括 getter 或 setter),则提供可以验证是否调用了预期事物的模拟或存根。如果 IFoo 上有可以测试的东西,您可以通过模拟对象传递已知值以在创建后进行测试。对象本身不需要公开可用以验证您是否通过了它们。

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

如果可能导致任何错误情况,我也会尝试强制执行这些情况,如果您为其中一个或两个对象传递 null 会发生什么,等等。当然,Foo 将单独测试,因此您可以在FooFactory 测试 Foo 做它应该做的。

于 2012-02-02T23:33:05.023 回答
0

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

另一方面,通常 DI 容器作为工厂工作,因此如果您使用的是工厂,则不需要工厂。

于 2012-02-02T23:33:53.197 回答