c# – 使用例如创建模拟和匿名对象的混合. Moq和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; } } 问题是我有一些代码可以访问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>(); 让我无法对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; } 解决方法
这实际上可以使用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; } } } 该解决方案提供了该问题的一般解决方案.但是,它还没有经过广泛测试,所以我很乐意得到反馈. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |