2012-02-18 90 views
6

我遇到了我的工作中的一类,看起来像这样:使用例如创建模拟和匿名对象的混合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设置为一些值。所以,当我使用起订量为这样:

var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock }; 

让我安装上GetSomeValue结果()方法,但所有属性都设置为null(使用安装程序设置所有的人()是相当繁琐的,因为真实对象是一个令人讨厌的数据对象,并且具有比上述简化示例更多的属性)。

因此在另一方面,使用AutoFixture这样的:

var fixture = new Fixture(); 
var anyMyObject = fixture.CreateAnonymous<MyObject>(); 

令我无以STUP到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; 
} 

回答

5

实际上可能与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())); 
    } 
} 

MockPostprocessorMockConstructorQueryMockRelay类在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; 
     } 
    } 
} 

该解决方案提供对问题的通用解决方案。但是,它没有经过广泛的测试,所以我很乐意获得关于它的反馈。

+0

这是一个很好的解决方案,特别是因为它涵盖了嵌套类型,但是,它是更普遍的嵌套类型,有没有什么办法来摆脱部分:T == typeof运算(的MyObject)| | t == typeof(MyParent)? 我想到的逻辑是这样的:“如果混合的嵌套类型是可嘲弄的,使其成为一个混合,否则使它成为一个匿名值”。 – 2012-02-19 12:30:15

+0

是的,只需要用更一般的东西来替代谓词即可。 – 2012-02-19 12:50:24

+0

谢谢!我尝试使用(t!= null &&!!..IsPrimitive &&(t.GetConstructors(BindingFlags.Public).Length!= 0 || t.GetConstructor(Type.EmptyTypes)!= null))的谓词上班!你能看到这里有什么遗漏吗?我会尽量给它一点测试,并尝试让你知道是否有任何事情发生。 – 2012-02-19 20:33:39

4

有可能是一个更好的原因,但这个工程:

var fixture = new Fixture(); 
var moq = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock }; 
moq.Setup(m => m.GetSomeValue()).Returns(3); 

fixture.Customize<MyObject>(c => c.FromFactory(() => moq.Object)); 

var anyMyObject = fixture.CreateAnonymous<MyObject>(); 

Assert.AreEqual(3, anyMyObject.GetSomeValue()); 
Assert.IsNotNull(anyMyObject.A); 
//... 

最初我试图用fixture.Register(() => moq.Object);而不是fixture.Customize,但它注册了crea与OmitAutoProperties() tor功能,所以它不适用于你的情况。

2

从3.20.0开始,您可以使用AutoConfiguredMoqCustomization。这将自动配置所有模拟,以便其成员的返回值由AutoFixture生成。

var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization()); 

var mock = fixture.Create<Mock<MyObject>>(); 

Assert.NotNull(mock.Object.A); 
Assert.NotNull(mock.Object.B); 
Assert.NotNull(mock.Object.C);