16

Después de todo lo que he leído sobre Dependency Injection y IoC, he decidido tratar de usar Windsor Container dentro de nuestra aplicación (es una aplicación web multicapa de 50K LOC, así que espero que no exagere). He utilizado una clase estática simple para envolver el contenedor y lo inicializo al iniciar la aplicación, que funciona bastante bien por ahora.¿Cuál debería ser la estrategia de las pruebas unitarias cuando se usa IoC?

Mi pregunta es sobre pruebas unitarias. Sé que DI va a hacer mi vida mucho más fácil allí al darme la posibilidad de inyectar implementaciones de prueba/simulacro de colaboradores de clase a la clase bajo prueba. Ya he escrito un par de pruebas con esta técnica y parece tener sentido para mí. De lo que no estoy seguro es de si debería usar IoC (en este caso Windsor Castle) también en pruebas unitarias (probablemente de alguna manera lo configure para devolver stubs/mocks para mis casos especiales) o si es mejor conectar todas las dependencias manualmente en las pruebas. ¿Qué piensas y qué práctica te ha funcionado?

+2

Duplicado: http://stackoverflow.com/questions/1465849/using-ioc-for-unittesting –

+0

Gracias, no pude encuéntrelo en cualquier parte;) –

Respuesta

20

No necesita contenedor DI en pruebas unitarias porque las dependencias se proporcionan a través de objetos simulados generados con marcos tales como Rhino Mocks o Moq. Entonces, por ejemplo, cuando está probando una clase que tiene una dependencia en alguna interfaz, esta dependencia generalmente se proporciona a través de la inyección del constructor.

public class SomeClassToTest 
{ 
    private readonly ISomeDependentObject _dep; 
    public SomeClassToTest(ISomeDependentObject dep) 
    { 
     _dep = dep; 
    } 

    public int SomeMethodToTest() 
    { 
     return _dep.Method1() + _dep.Method2(); 
    } 
} 

En su aplicación va a utilizar un marco DI pasar alguna implementación real de ISomeDependentObject en el constructor que podría tener por sí mismo dependen de otros objetos mientras que en una prueba de unidad se crea un objeto de burla, ya que sólo desea probar esta clase en aislamiento. Ejemplo con Rhino Mocks:

[TestMethod] 
public void SomeClassToTest_SomeMethodToTest() 
{ 
    // arrange 
    var depStub = MockRepository.CreateStub<ISomeDependentObject>(); 
    var sut = new SomeClassToTest(depStub); 
    depStub.Stub(x => x.Method1()).Return(1); 
    depStub.Stub(x => x.Method2()).Return(2); 

    // act 
    var actual = sut.SomeMethodToTest(); 

    // assert 
    Assert.AreEqual(3, actual); 
} 
+0

Suena razonable. Después de todo, esto es exactamente lo que estoy haciendo. Simplemente no estaba seguro de si es una buena práctica, especialmente cuando hay muchas dependencias y tengo que burlarme de ellas manualmente. –

+0

Eche un vistazo al automóvil del que hablé en mi respuesta Thomas, realmente simplifica las clases de prueba con muchas dependencias. :-) –

2

Acabo de escribir una aplicación de estilo y tamaño muy similar. No pondría ninguna inyección de dependencia en las pruebas unitarias porque no es lo suficientemente complicado como para ser necesario. Debe usar un marco de burla para crear sus simulacros (RhinoMocks/Moq).

También Automocking en Moq o Auto Mock Container en Rhinomocks simplificará la construcción de sus burlas aún más.

automático de burla le permite obtener objeto del tipo que desea probar sin creación de burla con la mano. Todas las dependencias se burlan automáticamente (suponiendo que sean interfaces) y inyectadas en el constructor de tipos. Si necesita, puede configurar el comportamiento esperado, pero no es necesario.

+0

Sí, pero ¿no es Auto Mock Container también un contenedor IoC? ;) Es solo que está más relajado con respecto a la resolución de dependencias: generará una implementación de servicios falsa por defecto sin arrojar excepciones para cosas no resueltas como lo hace el contenedor "correcto" de Windsor. –

+1

... y lo más importante, ¡solo se necesitan dos líneas para crear! El hecho de que no necesite registrar ninguna dependencia significa que es mucho más fácil que un IoC regular. Otra razón para usar el automóvil es abstraer la llamada al objeto bajo el constructor de la prueba. Por lo tanto, si la firma cambia, no tiene que cambiar muchas pruebas, lo que hace que sus clases de prueba sean menos frágiles. :-) –

3

Estoy trabajando en un proyecto ASP.NET MVC con aproximadamente 400 pruebas unitarias. Estoy usando Ninject para la inyección de dependencia y MBUnit para la prueba.

Ninject no es realmente necesario para las pruebas unitarias, pero reduce la cantidad de código que tengo que escribir. Solo tengo que especificar una vez (por proyecto) cómo deben crearse instancias de mis interfaces en lugar de hacer esto cada vez que inicializo la clase que se está probando.

Para ahorrar tiempo en la escritura de nuevas pruebas, he creado clases base de dispositivos de prueba con la mayor cantidad posible de código genérico de configuración. Los procedimientos de configuración en esas clases inicializan repositorios falsos, crean algunos datos de prueba y una identidad falsa para el usuario de prueba. Las pruebas unitarias solo inicializan datos que son demasiado específicos para entrar en procedimientos de configuración genéricos.

También estoy burlando de los objetos (en lugar de fingir) en algunas pruebas, pero encontré que los repositorios de datos falsos resultan en menos trabajo y pruebas más precisas.

Por ejemplo, sería más difícil comprobar si el método bajo prueba I confía correctamente todas las actualizaciones en el repositorio después de hacerlas cuando usa un simulacro de repositorio que cuando estoy usando un repositorio falso.

Trabajé un montón al principio, pero realmente me ayudó a ahorrar mucho tiempo a la larga.

0

Como Darin ya ha señalado, no necesita usar DI si tiene simulacros. (Sin embargo, DI también tiene otros beneficios, que incluyen, antes que nada, disminuir las dependencias en su código, lo que hace que su código sea mucho más fácil de mantener y ampliar a largo plazo).

Personalmente prefiero cablear todo en mis pruebas de unidad, confiando lo menos posible en marcos externos, archivos de configuración, etc.

Cuestiones relacionadas