将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都使用严格的行为进行初始化。该框架似乎非常灵活和可扩展,所以我确信这是一个简单的方法 - 我只是无法弄清楚如何。
答案 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());