c# - ¿Cuál debería ser la estrategia de las pruebas unitarias cuando se usa IoC?
unit-testing dependency-injection (4)
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. Debería usar un marco de burla para crear sus simulacros (RhinoMocks / Moq).
También Automocking en Moq o el Auto Mock Container en Rhinomocks simplificará la construcción de tus mock .
El simulacro automático le permite obtener objetos del tipo que desea probar sin configurar los simulacros a mano. Todas las dependencias se simulan automáticamente (asumiendo que son interfaces) y se inyectan en el constructor de tipos. Si lo necesita, puede configurar el comportamiento esperado, pero no tiene que hacerlo.
Después de todo lo que he leído sobre Dependency Injection e IoC, he decidido intentar utilizar Windsor Container dentro de nuestra aplicación (es una aplicación web de múltiples capas 50K LOC, así que espero que no sea una exageración allí). 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.
Mi pregunta es sobre la prueba unitaria. Sé que la DI me va a hacer la vida mucho más fácil allí, dándome la posibilidad de inyectar implementaciones de muñones / simulacros de los colaboradores de la clase a la clase bajo prueba. Ya he escrito un par de pruebas utilizando esta técnica y parece que tiene sentido para mí. De lo que no estoy seguro es si debería usar IoC (en este caso, el Castillo de Windsor) también en pruebas unitarias (probablemente de alguna manera configurarlo para devolver los talones / simulacros 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?
Como Darin ya ha señalado, no necesitas usar DI si tienes burlas. (Sin embargo, DI también tiene algunos otros beneficios, que incluyen, en primer lugar, disminuir las dependencias en su código, lo que hace que su código sea mucho más fácil de mantener y extender a largo plazo).
Personalmente prefiero cablear todo en mis pruebas unitarias, confiando lo menos posible en marcos externos, archivos de configuración, etc.
Estoy trabajando en un proyecto MVC de ASP.NET con aproximadamente 400 pruebas de unidad. Estoy usando Ninject para inyección de dependencias y MBUnit para pruebas.
Ninject no es realmente necesario para la prueba unitaria, pero reduce la cantidad de código que tengo que escribir. Solo tengo que especificar una vez (por proyecto) cómo se deben instanciar mis interfaces en lugar de hacerlo cada vez que inicializo la clase que se está probando.
Con el fin de ahorrar tiempo en la escritura de nuevas pruebas, he creado clases de base de pruebas con el código de configuración tan genérico como sea posible. 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 me estoy burlando de los objetos (en lugar de falsificar) en algunas pruebas, pero descubrí que falsificar los repositorios de datos da como resultado menos trabajo y pruebas más precisas.
Por ejemplo, sería más difícil verificar si el método bajo prueba confirma correctamente todas las actualizaciones en el repositorio después de hacerlas cuando utilizo un simulador de repositorio que cuando estoy usando un falso repositorio.
Fue un poco de trabajo configurar al principio, pero realmente me ayudó a ahorrar mucho tiempo a largo plazo.
No necesita el contenedor DI en pruebas unitarias porque las dependencias se proporcionan a través de objetos simulados generados con marcos como Rhino Mocks o Moq . Entonces, por ejemplo, cuando está probando una clase que depende de 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, utilizará un marco DI para pasar alguna implementación real de ISomeDependentObject
en el constructor, que podría tener dependencias en otros objetos mientras que en una prueba unitaria crea un objeto simulado porque solo desea probar esta clase de forma aislada. 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);
}