我有一个非常简单的微服务,在大多数情况下,我不运行,而是依靠单元测试和集成测试来确认实现是正确的,并将工作。最近,我进行了更改,单元测试通过,因此部署到CI环境(稍后运行的集成测试可以告诉我问题是什么)。在我的统一组件的注册中,我创建了一个工厂并传递了一组数组参数,例如:
var container = new UnityContainer();
container.RegisterType<IMyType, MyImpl1>(nameof(MyImpl1));
container.RegisterType<IMyType, MyImpl2>(nameof(MyImpl2));
container.RegisterType<IMyType, MyImpl3>(nameof(MyImpl3));
container.RegisterType<MyFactory>(
new InjectionConstructor(new ResolvedArrayParameter<IMyType>(
new ResolvedParameter<IMyType>(nameof(MyImpl1)),
new ResolvedParameter<IMyType>(nameof(MyImpl2)),
new ResolvedParameter<ISomeOtherType>(nameof(MyImpl3)),
));
对于那些老鹰眼的读者,你会发现最后一个已解决的参数的错误,这基本上是我想要测试的 - 当然,我可以在try-catch中包装依赖项的寄存器并记录事件失败,但我想在部署之前捕获它。
所以,我的问题很简单(或者不是,我的谷歌搜索证明是一次大规模的失败),我如何检查依赖关系是否正确解析并模拟应用程序的运行?
一种方法是首先将依赖注册移动到其自己的类中,无论该容器支持的特定版本是什么 - Windsor Facility,Unity扩展等。
这意味着我们不会在测试中复制容器设置。相反,我们正在测试我们在应用程序中实际使用的相同容器设置。
public class MyUnityExtension : UnityContainerExtension
{
protected override void Initialize()
{
Container.RegisterType<IMyType, MyImpl1>(nameof(MyImpl1));
Container.RegisterType<IMyType, MyImpl2>(nameof(MyImpl2));
Container.RegisterType<IMyType, MyImpl3>(nameof(MyImpl3));
}
}
如果您希望应用程序提供特定于环境的值,则可以在接口或类中定义这些值并将其添加到构造函数中,然后根据容器设置中的需要使用这些设置中的连接字符串等值。
public class MyUnityExtension : UnityContainerExtension
{
private readonly ISettings _settings;
public MyUnityExtension(ISettings settings)
{
_settings = settings;
}
...etc
}
现在,在单元测试中,您可以将扩展添加到容器中,并测试您是否能够解析特定的依赖项。
using (var container = new UnityContainer())
{
var extension = new MyUnityExtension();
container.AddExtension(extension);
var resolved = container.Resolve<IMyType>();
Assert.IsNotNull(resolved);
}
同时,您可以打包依赖注册,以便托管应用程序只能提供一些特定于环境的值,而不必配置容器本身。
因此,根据@ camilo-terevinto的注释,当您注册依赖项时,它会抛出异常 - 使用nunit测试下面的简化版本:
[Test]
public void AssertThatDependenciesCorrectlyMap()
{
Assert.DoesNotThrow(() =>
{
var container = new UnityContainer();
container.RegisterType<IMyType, MyImpl1>(nameof(MyImpl1));
container.RegisterType<IMyType, MyImpl2>(nameof(MyImpl2));
container.RegisterType<IMyType, MyImpl3>(nameof(MyImpl3));
container.RegisterType<MyFactory>(
new InjectionConstructor(new ResolvedArrayParameter<IMyType>(
new ResolvedParameter<IMyType>(nameof(MyImpl1)),
new ResolvedParameter<IMyType>(nameof(MyImpl2)),
new ResolvedParameter<ISomeOtherType>(nameof(MyImpl3)),
));
}
}