2012-02-18 6 views
6

natknąłem klasę podczas mojej pracy, która wygląda tak:Tworzenie hybrydy próbnego i anonimowego obiektu za pomocą np. Moq i 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; 
    } 
} 

Problem jest, że mam jakiś kod, który uzyskuje dostęp do A, B i C oraz wywołuje metodę GetSomeValue() (obecnie, powiedziałbym to nie jest dobry projekt, ale czasami moje ręce są związane ;-)). Chcę stworzyć próbę tego obiektu, który jednocześnie ma ustawione wartości A, B i C. Tak więc, gdy używam MOQ jako takie:

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

pozwala mi setup wynik na GetSomeValue() metoda, ale wszystkie właściwości są ustawione na null (i konfigurowania wszystkich z nich za pomocą programu konfiguracji() jest dość kłopotliwe, ponieważ obiekt rzeczywisty jest nieprzyjemnym obiektem danych i ma więcej właściwości niż w powyższym uproszczonym przykładzie).

Więc z drugiej strony, przy użyciu AutoFixture tak:

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

pozostawia mnie bez możliwości stup wezwanie do GetSomeValue metoda().

Czy istnieje sposób na połączenie tych dwóch elementów, uzyskanie anonimowych wartości i możliwość ustawienia wyników połączeń?

Edit

podstawie odpowiedzi nemesv jest, że pochodzi z następującej metody użytkową (mam nadzieję, mam to po prawej):

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; 
} 

Odpowiedz

5

Ten jest w rzeczywistości możliwy do wykonania z AutoFixture, ale wymaga nieco ulepszenia. Punkty rozciągliwości są wszędzie, ale przyznaję, że w tym przypadku rozwiązanie nie jest szczególnie łatwe do wykrycia.

Staje się jeszcze trudniejsze, jeśli chcesz pracować z typami zagnieżdżonymi/złożonymi.

Biorąc pod uwagę klasę MyObject powyżej, jak również ten MyParent Klasa:

public class MyParent 
{ 
    public MyObject Object { get; set; } 

    public string Text { get; set; } 
} 

nich jednostka testuje wszystkie wprost:

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)); 
    } 
} 

Co znajduje się w MockHybridCustomization? To:

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 i MockRelay klasy są zdefiniowane w AutoMoq extension do AutoFixture, więc trzeba dodać odwołanie do tej biblioteki. Należy jednak pamiętać, że nie jest wymagane dodawanie AutoMoqCustomization.

Klasa AutoExceptMoqPropertiesCommand jest również niestandardowy zbudowany na tę okazję:

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; 
     } 
    } 
} 

To rozwiązanie zapewnia ogólne rozwiązanie kwestii. Jednak nie został on dokładnie przetestowany, dlatego chciałbym uzyskać opinię na jego temat.

+0

Jest to miłe rozwiązanie, szczególnie, ponieważ obejmuje zagnieżdżone typy jednak, aby była ona bardziej ogólnie z zagnieżdżonych typów, czy jest jakiś sposób, aby pozbyć się części: t == typeof (MyObject) | | t == typeof (MyParent)? Logika, o której myślę, to coś w rodzaju: "jeśli typ zagnieżdżony hybrydy jest możliwy do opanowania, uczyń z niego hybrydę, inaczej uczyń ją wartością anonimową". –

+0

Tak, wystarczy zamienić predykat na coś bardziej ogólnego. –

+0

Dzięki! Próbowałem użyć predykatu (t! = Null &&! T.IsPrimitive && (t.GetConstructors (BindingFlags.Public) .Length! = 0 || t.GetConstructor (Type.EmptyTypes)! = Null) i wydaje się pracować! Czy widzisz, że czegoś tu nie ma? Postaram się trochę przetestować i spróbować powiedzieć, czy coś wyjdzie. –

4

Prawdopodobnie istnieje lepszy dlaczego, ale to działa:

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); 
//... 

Początkowo próbowałem użyć fixture.Register(() => moq.Object); zamiast fixture.Customize, ale rejestruje on krea Funkcja tor z OmitAutoProperties(), więc nie będzie działać dla Ciebie.

2

Od wersji 3.20.0 można użyć AutoConfiguredMoqCustomization. Spowoduje to automatyczną konfigurację wszystkich prób, tak aby wartości zwracane przez ich członków były generowane przez 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); 
Powiązane problemy