我可以在单元/集成测试中使用IoC容器吗?

时间:2012-08-21 19:05:24

标签: unit-testing integration-testing ioc-container

在解析某些接口时,我有一个IoC容器做了一些复杂的对象构造。我想在我的单元/集成测试中使用这些接口的实现。使用IoC容器在测试中解析这些接口是否有任何问题,或者在这种情况下是否应该手动构建实例?

1 个答案:

答案 0 :(得分:6)

当我们对一个类进行单元测试时,我们关注的是'该类是否按照我们的意愿执行'。 我们的出发点是一个完全构建的实例;我们如何到达那里没有一个单元测试问题,虽然它可能被认为是一个集成测试问题。

说我们有,

A
  A(IB b, IC c)

B : IB
  B(ID d)

C : IC

D : ID

当单元测试A时,B使用ID的事实应该没有实际意义(如果不是那么我们应该看看我们的接口。有一个访问IB.D.xxx不好),我们需要做的就是提供A与IB和IC的一些实现(模拟/存根)。

就A的单元测试而言,A实例是手动创建还是由容器创建并不重要。无论哪种方式我们都得到相同的对象。

只要我们将mocks作为第一级依赖项传递,那么在使用容器而不是手动创建对象时就没有保存。只有在我们使用IOC创建对象图时才会进行保存,但如果我们这样做,那么我们就会进行集成测试。这不是一件坏事,但我们需要明确我们的目标。

当对上述单元进行测试时,我们会为

创建单元测试

d  C  B(传入模拟/存根ID)  A(传入模拟/存根IC和IB)

当单元测试A时,我们不需要D的正确答案通过B到A 我们所关心的是A中的逻辑按预期工作,例如, A使用参数 y z 调用IB.x()并返回结果IB.x()的。如果我们检查我们得到了正确的答案(例如,依赖于D中的逻辑的答案)那么我们已经过单元测试并进入集成测试。

底线
只要我们正确隔离被测单元,被测单元是否由IOC容器或手工创建并不重要。如果我们使用容器来创建一个对象图,那么我们进入集成测试的好几率(和/或在类之间有太多耦合的问题 - 调用IB.D.xxx)

模拟集成测试

警告:以下部分内容取决于正在使用的IOC容器。使用Unity时,最后一次注册“获胜”。我不知道这对其他人来说也是如此。

在所讨论的极简主义系统中,我们有

一个      A(IB b)

B:IB

B是我们的“叶子”。它与外界交谈(例如,从网络流中读取)。 在进行集成测试时,我们想要模拟这个。

对于实时系统,我们使用CreateContainerCore()设置ServiceLocator。 这包括注册IB的“实时”实施。

当执行需要模拟IB版本的集成测试时,我们使用CreateContainerWithMockedExternalDependencies()设置容器,该包装包含CreateContainerCore()并为IB注册模拟对象。

下面的代码大大简化了,但形状扩展到了所需的类和依赖项。在实践中,我们有一个基本测试类,它有助于设置服务定位器,模拟/存根类,访问模拟以进行验证和其他内容保存(例如,每个测试不需要显式设置ServiceLocator提供程序)< / p>

[TestClass]
public class IocIntegrationSetupFixture {


    [TestMethod]
    public void MockedB() {

        ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(CreateContainerWithMockedExternalDependencies()));

        var a = ServiceLocator.Current.GetInstance<A>();
        Assert.AreEqual("Mocked B", a.GetMessage());

    }


    [TestMethod]
    public void LiveB() {

        ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(CreateContainerCore()));

        var a = ServiceLocator.Current.GetInstance<A>();
        Assert.AreEqual("Live B", a.GetMessage());
    }

    private IUnityContainer CreateContainerCore() {
        var container = new UnityContainer();
        container.RegisterType<IB, B>(new ContainerControlledLifetimeManager());
        return container;
    }

    private IUnityContainer CreateContainerWithMockedExternalDependencies() {
        var container = CreateContainerCore();

        var mockedB = new Mock<IB>();
        mockedB.SetupGet(mk => mk.Message).Returns("Mocked B");
        container.RegisterInstance<IB>(mockedB.Object);

        return container;
    }


    public class A  {

        private IB _b;
        public A(IB b) {
            _b = b;
        }

        public string GetMessage() {
            return _b.Message;
        }

    }

    public interface IB {
        string Message { get; }
    }

    private class B : IB {
        public string Message {
            get { return "Live B"; }
        }
    }

}