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

Ian*_*ton 3 integration-testing unit-testing ioc-container

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

Ala*_*anT 6

当我们对一个班级进行单元测试时,我们关心的是"班级是否按照我们的意愿行事".我们的出发点是一个完全构建的实例; 我们如何到达那里不是一个单元测试问题,虽然它可能被认为是一个集成测试问题.

说我们有,

A
  A(IB b, IC c)

B : IB
  B(ID d)

C : IC

D : ID
Run Code Online (Sandbox Code Playgroud)

当单元测试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用参数yz调用IB.x()并返回IB.x()的结果.如果我们检查我们得到了正确的答案(比如,一个依赖于D中的逻辑的答案),那么我们已经过单元测试并进入集成测试.

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

模拟集成测试

警告:以下部分内容取决于正在使用的IOC容器.使用Unity时,最后一次注册"获胜".我不知道这适用于其他人.

在我们所讨论的极简主义制度中

AA(IB b)

B:IB

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

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

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

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

[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"; }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)