【问题标题】:How can I make AutoMoqCustomization use Strict MockBehavior?如何使 AutoMoqCustomization 使用 Strict MockBehavior?
【发布时间】:2015-12-03 00:23:55
【问题描述】:

将 AutoFixture 与 AutoFixture.AutoMoq 包一起使用,我有时会发现测试没有配置为正确测试他们要测试的东西,但由于默认的(松散)模拟行为,问题从未被发现:

public interface IService
{
    bool IsSomethingTrue(int id);
}

void Main()
{
    var fixture = new Fixture()
        .Customize(new AutoMoqCustomization());
    var service = fixture.Freeze<Mock<IService>>();
    Console.WriteLine(service.Object.IsSomethingTrue(1)); // false
}

我想让 Mocks 使用 Strict 行为创建,因此我们不得不为我们期望调用的方法调用 Setup()。我可以像这样为每个单独的模拟执行此操作:

fixture.Customize<Mock<IService>>(c => c.FromFactory(() => new Mock<IService>(MockBehavior.Strict)));

但是在梳理了 AutoMoqCustomization() 的源代码以及各种 ISpecimenBuilder 和其他实现之后,我对让所有 Mocks 以严格行为初始化的最佳方法感到非常迷茫。该框架看起来非常灵活和可扩展,所以我确信有一种简单的方法可以做到这一点——我就是不知道怎么做。

【问题讨论】:

    标签: moq autofixture automoq


    【解决方案1】:

    没有简单的内置功能可以让你做这样的事情,但它不应该那么很难做到。

    基本上,您需要更改MockConstructorQuery,以便它调用采用MockBehavior 值的构造函数,并传入MockBehavior.Strict

    现在,您无法更改 MockConstructorQuery 中的该行为,但该类只有大约 9-10 行代码,因此您应该能够创建一个实现 @ 的新类987654329@ 以MockConstructorQuery 为起点。

    同样,您还需要创建一个与AutoMoqCustomization 几乎完全相同的自定义ICustomization,唯一的例外是它使用带有严格模拟配置的自定义IMethodQuery 而不是MockConstructorQuery。那是您需要编写的另外 7 行代码。

    综上所述,根据我的经验,使用严格的模拟是一个坏主意。它会使您的测试变得脆弱,并且您会浪费大量时间来修补“损坏”的测试。我只能建议你不要这样做,但现在我已经警告你了;这是你的脚。

    【讨论】:

    • 感谢您的反馈。我会看看我能不能让它工作。关于严格的模拟:如果模拟是真正的“模拟”,我认为你的观点是有效的。不幸的是,我的大多数模拟实际上都服务于 stubs 的目的,它们应该返回一个值。如果 SUT 依赖于从存根返回的数据,而我没有设置存根,那么在最好的情况下测试会失败,在最坏的情况下测试会因为错误的原因通过。无论哪种情况,我都希望立即看到显示哪个存根需要配置的代码行,而不必推断 NRE 的来源。
    • 我发现那篇文章的示例非常做作:破坏 CQS 以创建并返回一个用户对象,该对象甚至没有提供给 GetUser 方法的 ID。这些不是我通常看到的对方法进行的更改。我经常看到的更改无论如何都需要对单元测试进行更改,即使它们的编写方式与那篇文章中的最后一个正确示例一样。发生这种情况时,我发现 Strict 模拟的快速失败行为可以节省大量时间,而且更重要的是,有助于确保我的单元测试正在测试他们声称要测试的内容。
    • @MarkSeemann 不幸的是,Moq 不区分查询和命令。对查询有严格要求是可以的,IMO github.com/moq/moq4/issues/1056
    【解决方案2】:

    对于那些感兴趣的人,您可以在下方找到@MarkSeemann 的回复已翻译成代码。我很确定它没有涵盖所有用例,并且没有经过大量测试。但这应该是一个很好的起点。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Moq;
    using Ploeh.AutoFixture;
    using Ploeh.AutoFixture.AutoMoq;
    using Ploeh.AutoFixture.Kernel;
    
    namespace ConsoleApplication1
    {
        public class StrictAutoMoqCustomization : ICustomization
        {
            public StrictAutoMoqCustomization() : this(new MockRelay()) { }
    
            public StrictAutoMoqCustomization(ISpecimenBuilder relay)
            {
                // TODO Null check params
                Relay = relay;
            }
    
            public ISpecimenBuilder Relay { get; }
    
            public void Customize(IFixture fixture)
            {
                // TODO Null check params
                fixture.Customizations.Add(new MockPostprocessor(new MethodInvoker(new StrictMockConstructorQuery())));
                fixture.ResidueCollectors.Add(Relay);
            }
        }
    
        public class StrictMockConstructorMethod : IMethod
        {
            private readonly ConstructorInfo ctor;
            private readonly ParameterInfo[] paramInfos;
    
            public StrictMockConstructorMethod(ConstructorInfo ctor, ParameterInfo[] paramInfos)
            {
                // TODO Null check params
                this.ctor = ctor;
                this.paramInfos = paramInfos;
            }
    
            public IEnumerable<ParameterInfo> Parameters => paramInfos;
    
            public object Invoke(IEnumerable<object> parameters) => ctor.Invoke(parameters?.ToArray() ?? new object[] { });
        }
    
        public class StrictMockConstructorQuery : IMethodQuery
        {
            public IEnumerable<IMethod> SelectMethods(Type type)
            {
                if (!IsMock(type))
                {
                    return Enumerable.Empty<IMethod>();
                }
    
                if (!GetMockedType(type).IsInterface && !IsDelegate(type))
                {
                    return Enumerable.Empty<IMethod>();
                }
    
                var ctor = type.GetConstructor(new[] { typeof(MockBehavior) });
    
                return new IMethod[]
                {
                    new StrictMockConstructorMethod(ctor, ctor.GetParameters())
                };
            }
    
            private static bool IsMock(Type type)
            {
                return type != null && type.IsGenericType && typeof(Mock<>).IsAssignableFrom(type.GetGenericTypeDefinition()) && !GetMockedType(type).IsGenericParameter;
            }
    
            private static Type GetMockedType(Type type)
            {
                return type.GetGenericArguments().Single();
            }
    
            internal static bool IsDelegate(Type type)
            {
                return typeof(MulticastDelegate).IsAssignableFrom(type.BaseType);
            }
        }
    }
    

    用法

    var fixture = new Fixture().Customize(new StrictAutoMoqCustomization());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-27
      • 2021-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-22
      相关资源
      最近更新 更多