我对这两个库都不熟悉,在将它们用于大型项目之前,我需要澄清在单元测试中使用低代码自动锁定的选项。
在谷歌上花了一些时间后,我得出结论,与其他一些IOC/mock产品配对不同,LightInject+Nsubstitute没有现成的插件库来简化在单元测试的安排阶段中不做任何默认mock的声明。
我已经阅读了LightInject文档,关于如何使用临时增强的模拟对象覆盖LightInject容器,仅用于单元测试的范围,但是关于单元测试可能触及的所有不做任何事情的默认隔离模拟。是否有一种方法可以在LightInject容器内自动创建它们?
我正在寻找的内部IOC容器行为是:
public class LightInject.ServiceContainer
{
..
public T GetInstance<T)
{
if (( this.RegisteredInterfaces.Any( i => i.Itype == T ) == false )
&& ( this.TemporaryUnitTestOverrides.Any( i => i.Itype == T ) == false ))
&& ( /* this container is configured with an automocking delegate */ ))
return autoMockCreatorDelegate<T>.Invoke();
}
看起来LightInject的IProxy和Interceptors提供了一些内部模拟对象构建块,但是相比之下,Nsubstitute库功能齐全。
澄清默认情况下什么都不做mock和增强mock的含义。
// default do nothing mock
var calculator = Substitute.For<ICalculator>();
// Enhanced mock that will return 3 for .Add(1,2)
var calculator = Substitute.For<ICalculator>();
calculator.Add(1, 2).Returns(3);
显然,第二种增强的模拟类型需要在每个单元测试中本地生成。
我是LightInject的作者,真的很想帮助你。
让我调查一下,然后给你答复。与此同时,你可能想看看这个图书馆在LightInject。AutopMoq是第三方对LightInject容器的贡献。它使用Moq而不是NSubstitute,但一般概念应该与您所要求的相似。
话虽如此,我之前做了一些工作,进一步简化了自动锁定,并将研究它,看看如何将其与NSubstitute集成。
编辑这是一个超级简单的自动锁定实现,可以与任何"替代"框架一起工作。
using System.Diagnostics;
using LightInject;
using NSubstitute;
public interface IFoo { }
class Program
{
static void Main(string[] args)
{
var serviceContainer = new ServiceContainer();
serviceContainer.RegisterFallback((type, s) => true, request => CreateMock(request.ServiceType));
var foo = serviceContainer.GetInstance<IFoo>();
Debug.Assert(foo is IFoo);
}
private static object CreateMock(Type serviceType)
{
return Substitute.For(new Type[] { serviceType }, null);
}
}
敬上
Bernhard Richter
一些反馈,正如我在评论中承诺的那样。我采用了LightInject作者的建议,在一些简单的单元测试中取得了成功。
在获得基本工作后,我决定将Ioc服务模拟设置代码隐藏在基类中加上我称为MockingContext的东西,最终结果是更干净,更轻的单元测试代码。mock上下文类还确保,对于作为短期自动锁覆盖传递给Ioc服务的每个Nsubstitute配置的模拟类型,都有一个匹配的lightinject . service。EndMocking(T)调用。这消除了配置的模拟可能会污染后续单元测试的自动模拟假设的危险。
在示例中,ClassC依赖于IFooA和IFooB(没有构造函数注入)。对于下面的单元测试,IFooA在没有显式代码的情况下由LightInject自动模拟,而IFooB是通过Nsubstitute调用配置的,并且在MockingContext.Add<>()方法中传递给LightInject。
[TestClass]
public class UnitTest1 : AutoMocking
{
[TestMethod]
public void Test_1()
{
using (var mc = MockingContext)
{
// No need to mention IFooA here, LightInject will auto mock
// any interface not previously declared to it.
// Given
var mockB = mc.Add<IFooB>();
mockB.MethodY().Returns("Mock Value OOO");
var sut = new ClassC();
// When
var testResult = sut.MethodZ();
// Then
Assert.AreEqual(testResult, "MethodZ() received=Mock Value OOO");
}
}