使用工厂方法创建的私有构造函数的单元测试类

时间:2014-05-30 15:09:21

标签: c# unit-testing

我知道有很多类似的问题被提出/回答,但我有一个特殊的情况,我无法解决。

我们正在创建一个用于其他应用程序的库,该库以用户实例化和使用的主库类为中心。这个类有各种各样的依赖,部分设计要求规定我们不希望库的用户处理创建依赖对象图或处理初始化任务,所以我们已经使类构造函数私有,并且它们从静态方法创建实例,静态方法又创建类的实例并使用IoC容器解析依赖项。像这样:

    public static ILibrary CreateInstance()
    {

        Library lib = new Library(
            IoC.Instance.Resolve<IDependency1>(),
            IoC.Instance.Resolve<IDependency2>(),
            // etc
            );

        lib.Initialize();

        return lib;
    }

    private Library(IDependency1, IDependency2, etc)
    {
        // some ctor setup here
    }

测试它的核心问题是CreateInstance()方法调用IoC容器的静态实例来处理依赖项解析,这阻止我们将模拟依赖项注入到对象图中。

本着“如果你无法弄清楚如何测试它,你可能有一个设计缺陷”的精神,我想知道是否有人能指出正确的方法来解决这个问题。

3 个答案:

答案 0 :(得分:4)

您至少有两个选项:


public构造函数

您提到您不希望您的库用户必须处理初始化和对象图的创建。这是隐藏工厂背后的初始化事项的一个很好的理由 - 但这并不妨碍允许用户处理这些细节。

您的测试可以被视为代码的消费者。如果测试希望能够以某种方式配置对象(例如,通过依赖注入),那么它很可能其他消费者会分享这种兴趣。

通过提供工厂方法,您可以让消费者创建库,而无需担心组成其对象图。

通过提供public构造函数,您可以为消费者提供选项,以使用特定的对象图创建库。


internal构造函数

通常用于测试共享库的私有组件的一种方法是expose relevant components specifically to the test project

不是将构造函数指定为private,而是将其标记为internal。然后,修改共享库的AssemblyInfo以包含以下程序集属性:

    [assembly: InternalsVisibleTo("MyLibraryTestProject")]

标记为internal的类和方法通常仅对其定义的项目可见。上述更改明确允许您的测试项目查看(并测试)它们。

答案 1 :(得分:0)

这是我提出的方法,它起作用但并不完全理想。我仍然很想知道其他想法。

在工厂方法中,我没有调用静态IoC.Instance,而是在库本身上创建了一个静态属性,如果没有设置它,它会返回库的IoC容器,或者允许我用一个装满了容器的容器覆盖它。嘲笑。基本上是:

private static Container _container;
public static Container Container
{
    get { if (_container ==null) return IoC.Instance; return _container; }
    set { _container = value; }
}

然后在单元测试中我只是将容器与用于测试的容器交换。

我唯一不喜欢的是它暴露了我不希望图书馆消费者使用的公共财产,这使API设计有点混乱。但是我可能只是选择尼特。

答案 2 :(得分:0)

指向初始化库,如下所示:

    Library lib = new Library(
        IoC.Instance.Resolve<IDependency1>(),
        IoC.Instance.Resolve<IDependency2>(),
        // etc
        );

是否依赖注入使您能够在那里获得测试依赖性。配置IoC对象以提供正确的依赖关系。看来你必须在测试时制作一个模拟IoC对象,用它来注入测试依赖项。您可以使用模拟框架(如Fakes,Mock等)使这更容易。