如何使AutoMoqCustomization使用Strict MockBehavior?

时间:2015-12-03 00:23:56

标签: moq autofixture automoq

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

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,因此我们不得不为我们期望调用的方法调用Setup()。我可以为每个个人模拟这样做:

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

但是在梳理了AutoMoqCustomization()和各种ISpecimenBuilder以及其他实现的源代码之后,我很遗憾只是让所有Mocks都使用严格的行为进行初始化。该框架似乎非常灵活和可扩展,所以我确信这是一个简单的方法 - 我只是无法弄清楚如何。

2 个答案:

答案 0 :(得分:4)

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

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

现在,您无法在MockConstructorQuery更改该行为,但该类只有大约9-10行代码,因此您应该能够创建一个新类使用IMethodQuery作为起点来实现MockConstructorQuery

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

所有这一切,根据我的经验,使用严格的模拟是一个坏主意。它会使你的测试变得脆弱,你会浪费很多时间修补“破损”的测试。我只能建议你不要这样做,但现在我已经警告过你;这是你的脚。

答案 1 :(得分:1)

对于有兴趣的人,在下面你可以找到@ 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());