2010-01-28 27 views
6

Estoy buscando formas de hacer lo siguiente más conciso.Obtención de DRY con Rhino Mocks

public class MyTests 
{ 
    IPresenter presenter; 

    [SetUp] 
    public void SetUp() 
    { 
     presenter = MockRepository.GenerateStub<IPresenter>(); 
    } 

    ... 
} 

En particular, especificar el tipo de nuevo al crear el simulacro parece redundante. Por ejemplo, yo puedo escribir de esta manera y utilizar la reflexión para obtener el tipo y crear el talón de forma automática:

public class MyTests 
{ 
    IPresenter presenter; 

    [SetUp] 
    public void SetUp() 
    { 
     Stub(x => x.presenter); 
    } 

    void Stub(Expression<Func<MyTests, object>> expression) 
    { 
     ... 
    } 
} 

esto iba a funcionar, pero el compilador ya no puedo detectar es asignado y comienza la emisión de advertencias de que el presentador. Esto también hace que ReSharper sea muy infeliz.

¿Alguien puede sugerir un mejor enfoque?

Respuesta

5

Esto puede ser controvertido, pero yo prefiero legibilidad, en lugar de DRY-ness * en pruebas unitarias.

En otras palabras, los métodos de configuración no existen en mis pruebas unitarias. Solo se usan para pruebas de integración. Creo que XUnit.NET adopta esta postura también.

Para responder a su pregunta, realmente no me preocuparía configurar presentadores falsos en cada una de sus pruebas que lo requieran. Algunas pruebas pueden no necesitar un presentador falso, por lo tanto, no es necesario tener una configuración antes de ejecutar la prueba.

** Naturalmente, las pruebas de mi unidad abarcan una media de diez líneas, si esto aumenta o el alcance de la configuración (siguiendo AAA - Organizar, Actuar) es grande, solo entonces eliminaré la duplicación y crearé ayuda métodos. Para aclarar este punto, para pruebas más limpias, puede crear una clase de prueba base que contenga los métodos de ayuda y otro código de configuración. *

+1

Principalmente estoy de acuerdo con esto (de ahí el +1), pero también me gustaría sugerir que se puede crear una clase interna para las pruebas con simulacros si va a tener muchos casos de prueba usándolos. – jonnii

+0

@jonni - Me ganaste a mi edición. Pero estoy de acuerdo. – Finglas

+0

@finglas Es un círculo de acuerdo. Vamos a dar palmaditas en la parte posterior;) – jonnii

3

Sí, no use [Setup] y las variables miembro en absoluto, escriba Fixture Objects con métodos de creación en su lugar.

El Objeto Fixture simplemente mantendría el simulacro apropiado y otras partes del Accesorio.

Yo personalmente uso AutoFixture as a Fixture Object, y lo han establecido como un automático de contenedores que imita para arrancar, por lo que no tiene que escribir ningún código maqueta a menos que necesite definir explícitamente un cierto comportamiento.

Aquí es una reciente prueba de unidad de muestra:

[TestMethod] 
public void DeleteProductWillDeleteProductFromRepository() 
{ 
    // Fixture setup 
    var fixture = new ServiceFixture(); 
    var id = fixture.CreateAnonymous<int>(); 
    var repMock = fixture.FreezeMoq<ProductRepository>(); 

    var sut = fixture.CreateAnonymous<ProductManagementService>(); 
    // Exercise system 
    sut.DeleteProduct(id); 
    // Verify outcome 
    repMock.Verify(r => r.DeleteProduct(id)); 
    // Teardown 
} 

En este caso, repMock es creado por Moq, pero podría haber configurarlo para usar burla de Rhino en su lugar.

+1

sin [Configuración] o [desmontaje], xUnit FTW! – mxmissile

+0

Está intercambiando una variable de miembro + inicializador para configuración adicional en cada método que se sumará a más de unas pocas pruebas. Auto burlarse es algo que debería considerar, gracias. –

0

Es un dolor general con C#, que no infiere el tipo del resultado del método (a diferencia de Java), y es doloroso en muchas situaciones (solo para dar otro ejemplo, donde desea implementar un método de deserialización). Personalmente, no me gusta usar la palabra clave var, porque quiero ver exactamente cuál es el tipo de mis objetos, preferiría omitir el nombre del tipo en la plantilla.

De todos modos, si mis pruebas son agradables y pequeñas, tiendo a inicializar todos los simulacros dentro de cada caso de prueba. De esta forma, puede ver todas las pruebas en separación de las otras pruebas e inmediatamente ver qué está sucediendo allí. Si se alargan, utilizo la forma desagradable que pegaste al principio de tu pregunta.

1

Michael Feathers tiene una vista excepcional de esto (ver su presentación http://www.ndc2010.no/index.aspx?id=361621).Cree Builders y use esto en las pruebas en lugar de todo tipo de cosas de configuración.

igual:

//The Class to Test 
    public class ObjectY 
    { 
     public string DoThis(IObjectX objectX) 
     { 
      return objectX.id + objectX.name; 
     } 
    } 


    [Test] 
    //The test 
    public void CreaeteTestData() 
    { 
     //Almost prosa style creation of test data 
     var testData = new ObjectXBuilder().WithId(123).WithName("ABC").Build(); 

     Assert.That(new ObjectY().DoThis(testData), Is.EqualTo("123ABC")); 

    } 


    //The Builder class - Provides easy creation testdata. 
    internal class ObjectXBuilder 
    { 
     private MockRepository _mockRepository; 
     private IObjectX _objectX; 

     public ObjectXBuilder() 
     { 
      _mockRepository = new MockRepository(); 
      _objectX = _mockRepository.Stub<IObjectX>(); 
     } 

     public ObjectXBuilder WithName(string name) 
     { 
      _objectX.name = name; 
      return this; 
     } 

     public ObjectXBuilder WithId(long id) 
     { 
      _objectX.id = id; 
      return this; 
     } 

     public IObjectX Build() 
     { 
      _mockRepository.ReplayAll(); 
      return _objectX; 
     } 

    } 
Cuestiones relacionadas