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