2012-02-18 7 views
6

me encontré con una clase durante mi trabajo que tiene este aspecto:Crear un híbrido de un simulacro y un objeto anónimo utilizando, p. Moq y 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; 
    } 
} 

El tema es que tengo algo de código que tiene acceso A, B y C y llama al método GetSomeValue() (ahora, diría este no es un buen diseño, pero algunas veces tengo las manos atadas ;-)). Quiero crear un simulacro de este objeto que, al mismo tiempo, tiene A, B y C establecidos en algunos valores. Por lo tanto, cuando se utiliza el moq como tal:

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

me permite configurar un resultado en GetSomeValue() método, pero todas las propiedades se establecen en NULL (y la creación de todos ellos utilizando la Configuración() es bastante engorroso, ya que el objeto real es un objeto de datos desagradable y tiene más propiedades que en el ejemplo simplificado anterior).

Así que, por otro lado, el uso de AutoFixture así:

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

me deja sin la capacidad de STUP una llamada al método GetSomeValue().

¿Hay alguna forma de combinar los dos, tener valores anónimos y la capacidad de configurar los resultados de las llamadas?

Editar

Sobre la base de la respuesta de nemesv, que deriva el siguiente método de utilidad (espero que me dieron bien):

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

Respuesta

5

Esto es realmente posible de hacer con AutoFixture, pero requiere un poco de ajuste. Los puntos de extensibilidad están todos allí, pero admito que en este caso, la solución no es particularmente detectable.

Se vuelve aún más difícil si desea que funcione con tipos anidados/complejos.

Teniendo en cuenta la clase MyObject anteriormente, así como esta clase MyParent:

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

    public string Text { get; set; } 
} 

estos unidad de prueba todos pase:

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

¿Qué hay en MockHybridCustomization? Este:

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

El MockPostprocessor, MockConstructorQuery y MockRelay clases se definen en el AutoMoq extension a AutoFixture, por lo que tendrá que añadir una referencia a esta biblioteca. Sin embargo, tenga en cuenta que no es necesario agregar el AutoMoqCustomization.

La clase AutoExceptMoqPropertiesCommand está también hecha a la medida para la ocasión:

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

Esta solución proporciona una solución general a la pregunta. Sin embargo, no se ha probado exhaustivamente, por lo que me gustaría recibir comentarios al respecto.

+0

Esta es una buena solución, sobre todo porque cubre tipos anidados, sin embargo, para que sea más general con tipos anidados, ¿hay alguna manera de deshacerse de la pieza: t == typeof (MiObjeto) | | t == typeof (MyParent)? La lógica en la que estoy pensando es algo así como: "si el tipo anidado del híbrido es burlable, hágalo híbrido, de lo contrario, conviértalo en un valor anónimo". –

+0

Sí, simplemente reemplace el predicado con algo más general. –

+0

Gracias! Intenté usar un predicado de (t! = Null &&! T.IsPrimitive && (t.GetConstructors (BindingFlags.Public) .Length! = 0 || t.GetConstructor (Type.EmptyTypes)! = Null)) y parece ¡trabajar! ¿Puedes ver algo que falta aquí? Trataré de probarlo un poco e intentaré avisarte si sale algo. –

4

Probablemente hay una mejor por qué, pero esto funciona:

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

Inicialmente traté de usar fixture.Register(() => moq.Object); en lugar de fixture.Customize pero registra la creación función tor con OmitAutoProperties() por lo que no funcionaría para su caso.

2

A partir de 3.20.0, puede usar AutoConfiguredMoqCustomization. Esto configurará automáticamente todos los simulacros para que los valores de retorno de sus miembros sean generados por 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); 
Cuestiones relacionadas