Grz*_*ski 6 c# moq mocking anonymous-objects autofixture
我在工作期间遇到的课程如下:
public class MyObject
{
public int? A {get; set;}
public int? B {get; set;}
public int? C {get; set;}
public virtual int? GetSomeValue()
{
//simplified behavior:
return A ?? B ?? C;
}
}
Run Code Online (Sandbox Code Playgroud)
问题是我有一些代码可以访问A,B和C并调用GetSomeValue()方法(现在,我认为这不是一个好的设计,但有时我的双手并列;-)).我想创建一个这个对象的模拟,同时将A,B和C设置为某些值.所以,当我使用moq时:
var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
Run Code Online (Sandbox Code Playgroud)
让我在GetSomeValue()方法上设置一个结果,但所有属性都设置为null(使用Setup()设置所有属性非常麻烦,因为真实对象是一个讨厌的数据对象,并且具有比上面更多的属性简化的例子).
所以另一方面,使用像这样的AutoFixture:
var fixture = new Fixture();
var anyMyObject = fixture.CreateAnonymous<MyObject>();
Run Code Online (Sandbox Code Playgroud)
让我无法对GetSomeValue()方法进行调用.
有没有办法将两者结合起来,拥有匿名值和设置呼叫结果的能力?
编辑
根据nemesv的答案,我推导出以下实用方法(希望我做对了):
public static Mock<T> AnonymousMock<T>() where T : class
{
var mock = new Mock<T>();
fixture.Customize<T>(c => c.FromFactory(() => mock.Object));
fixture.CreateAnonymous<T>();
fixture.Customizations.RemoveAt(0);
return mock;
}
Run Code Online (Sandbox Code Playgroud)
这是实际可能的AutoFixture做的,但它确实需要一些调整.可扩展性点都在那里,但我承认在这种情况下,解决方案不是特别容易被发现的.
如果您希望它与嵌套/复杂类型一起使用会变得更加困难.
鉴于MyObject上面的类,以及这个MyParent类:
public class MyParent
{
public MyObject Object { get; set; }
public string Text { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这些单元测试全部通过:
public class Scenario
{
[Fact]
public void CreateMyObject()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyObject>();
Assert.NotNull(actual.A);
Assert.NotNull(actual.B);
Assert.NotNull(actual.C);
}
[Fact]
public void MyObjectIsMock()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyObject>();
Assert.NotNull(Mock.Get(actual));
}
[Fact]
public void CreateMyParent()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyParent>();
Assert.NotNull(actual.Object);
Assert.NotNull(actual.Text);
Assert.NotNull(Mock.Get(actual.Object));
}
[Fact]
public void MyParentIsMock()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyParent>();
Assert.NotNull(Mock.Get(actual));
}
}
Run Code Online (Sandbox Code Playgroud)
什么是MockHybridCustomization?这个:
public class MockHybridCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new MockPostprocessor(
new MethodInvoker(
new MockConstructorQuery())));
fixture.Customizations.Add(
new Postprocessor(
new MockRelay(t =>
t == typeof(MyObject) || t == typeof(MyParent)),
new AutoExceptMoqPropertiesCommand().Execute,
new AnyTypeSpecification()));
}
}
Run Code Online (Sandbox Code Playgroud)
的MockPostprocessor,MockConstructorQuery而MockRelay类是在定义AutoMoq扩展到AutoFixture,所以你需要添加一个引用到这个库.但请注意,不需要添加AutoMoqCustomization.
该AutoExceptMoqPropertiesCommand课程也是为这个场合定制的:
public class AutoExceptMoqPropertiesCommand : AutoPropertiesCommand<object>
{
public AutoExceptMoqPropertiesCommand()
: base(new NoInterceptorsSpecification())
{
}
protected override Type GetSpecimenType(object specimen)
{
return specimen.GetType();
}
private class NoInterceptorsSpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var fi = request as FieldInfo;
if (fi != null)
{
if (fi.Name == "__interceptors")
return false;
}
return true;
}
}
}
Run Code Online (Sandbox Code Playgroud)
该解决方案提供了该问题的一般解决方案.但是,它还没有经过广泛测试,所以我很乐意得到反馈.