【问题标题】:Automocking with LightInject plus Nsubstitute, how?使用 LightInject 和 Nsubstitute 进行自动模拟,怎么样?
【发布时间】:2014-05-03 13:31:57
【问题描述】:

我对这两个库都是新手,在将它们用于大型项目之前,我需要澄清我的单元测试中低代码工作量自动模拟的选项。

在 Google 上花了一些时间后,我得出的结论是,与其他一些 IOC/Mocking 产品配对不同,LightInject+Nsubstitute 没有现成的插件库来简化在排列阶段的无操作默认模拟的声明一个单元测试。

我已阅读 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 库功能齐全。

澄清我的意思是默认不做任何模拟和增强模拟。

   // 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);

显然,第二种增强的模拟类型需要在每个单元测试中本地制作。

【问题讨论】:

    标签: ioc-container nsubstitute automocking light-inject


    【解决方案1】:

    我是 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 感谢您的快速而权威的回复,您的示例代码正是我所希望的解决方案类型。我将创建一些原型单元测试并在这里报告。
    【解决方案2】:

    我在对已接受答案的评论中承诺的一些反馈。我在一些简单的单元测试中成功应用了 LightInject 作者的建议。

    在完成基础工作后,我决定将 Ioc 服务模拟设置代码隐藏在一个基类中,再加上我称之为 MockingContext 的东西,最终结果是更简洁的单元测试代码。模拟上下文类还确保将 foreach Nsubstitute 配置的模拟类型作为短期自动模拟覆盖传递给 Ioc 服务,存在匹配的 LightInjet.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");
            }
        }
    

    【讨论】:

    • 太棒了!如果您可以共享 MockingContext 类以便未来的读者可以利用它,那就太好了:)
    • @seesharper - 是的,一旦问题得到解决,我应该在您的主要 Light Inject 站点上提出该问题。进一步的测试表明 LightInjet.Service.EndMocking() 不起作用,并且手动模拟的对象正在渗透到下面的单元测试中。当自动模拟生效(RegisterFallback)并且没有先前的具体注册类型供服务展开时,也许 EndMocking() 会感到困惑?这不是一个重大挫折,因为我已经在我的 MockingContext dispose 方法中修补了 LightInject 服务重新创建。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 2016-02-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多