2012-02-18 95 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);