4

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

4

1 回答 1

7

当我们对一个类进行单元测试时,我们关心的是“类是否按照我们的意愿去做”。我们的出发点是一个完全构造的实例;我们如何到达那里不是单元测试问题,尽管它可能被认为是集成测试问题。

说我们有,

A
  A(IB b, IC c)

B : IB
  B(ID d)

C : IC

D : ID

其中:
IB是 的接口B
IC是 的接口C
ID是 的接口D

在进行单元测试A时,BusesID应该是没有实际意义的(如果不是,那么我们应该查看我们的接口。拥有A访问权限IB.D.xxx()并不好),我们需要做的就是提供A一些实现(模拟/存根)IBand IC

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

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

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

 D
 C
 B (passing in a mocked/stubbed ID)
 A (passing in mocked/stubbed IC and IB)

在进行单元测试时A,我们不需要正确的答案D来传递BA.
我们只关心 A 中的逻辑是否按预期工作,例如,使用参数yzA调用并返回 的结果IB.x()IB.x()。如果我们正在检查我们是否得到了正确的答案(例如,一个取决于逻辑的答案D),那么我们已经通过了单元测试并进入了集成测试。

底线 只要我们正确隔离了被测单元,被测
单元是由 IOC 容器创建还是由人工创建都没有关系。如果我们使用容器来创建对象图,那么我们进行集成测试的可能性很大(和/或存在类之间过多耦合的问题 -调用)AIB.D.xxx()

模拟集成测试

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

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

A 
  A (IB b)

B : IB

B是我们的“叶子”。它与外界对话(例如,从网络流中读取)。在集成测试时,我们想模拟它。

对于实时系统,我们使用CreateContainerCore(). 这包括注册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"; }
        }
    }

}
于 2012-08-22T09:05:44.360 回答