我在工作期间遇到的课程如下:
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; } }
问题是我有一些代码可以访问A,B和C并调用GetSomeValue()方法(现在,我认为这不是一个好的设计,但有时我的双手并列;-)).我想创建一个这个对象的模拟,同时将A,B和C设置为某些值.所以,当我使用moq时:
var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
让我在GetSomeValue()方法上设置一个结果,但所有属性都设置为null(使用Setup()设置所有属性非常麻烦,因为真实对象是一个讨厌的数据对象,并且具有比上面更多的属性简化的例子).
所以另一方面,使用像这样的AutoFixture:
var fixture = new Fixture(); var anyMyObject = fixture.CreateAnonymous<MyObject>();
有没有办法将两者结合起来,拥有匿名值和设置呼叫结果的能力?
编辑
根据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; }
解决方法
这实际上可以使用AutoFixture,但它确实需要一些调整.可扩展性点都在那里,但我承认在这种情况下,解决方案不是特别容易被发现的.
如果您希望它与嵌套/复杂类型一起使用会变得更加困难.
给定上面的MyObject类,以及此MyParent类:
public class MyParent { public MyObject Object { get; set; } public string Text { get; set; } }
这些单元测试全部通过:
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)); } }
什么是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())); } }
MockPostprocessor,MockConstructorQuery和MockRelay类在AutoMoq extension到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; } } }