5

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

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

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

4

2 回答 2

8

I am the author of LightInject and would really like to help you out.

Let me look into this and get back to you. In the meanwhile you might want to check out this library at LightInject.AutopMoq which is a third party contribution to the LightInject container. It uses Moq instead of NSubstitute, but the general concept should be similar to what you are asking for.

That being said, I did some work a while ago that simplifies automocking even further and will take a look at it it and see how that can be integrated with NSubstitute.

Edit

This is a super simple automocking implementation that works with any "substitute" framework.

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

Best regards

Bernhard Richter

于 2014-05-04T08:56:34.447 回答
1

我在对已接受答案的评论中承诺的一些反馈。我在一些简单的单元测试中成功地应用了 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");
        }
    }
于 2014-05-08T09:54:17.400 回答