2010-01-20 25 views
33

En mi trabajo estamos usando Moq para burlarse y Unity para un contenedor IOC. Soy bastante nuevo en esto y no tengo muchos recursos en el trabajo para ayudarme a determinar las mejores prácticas que debería usar.Forma correcta de simular objetos del repositorio para pruebas unitarias usando Moq y Unity

En este momento, tengo un grupo de interfaces de repositorio (por ejemplo, IRepository1, IRepository2 ... IRepository4) que un proceso en particular necesita usar para hacer su trabajo.

En el código real puedo determinar todos los objetos de IRepository usando el contenedor IOC y usando el método RegisterType().

Estoy tratando de encontrar la mejor manera de poder probar el método que necesita los 4 repositorios mencionados.

Estaba pensando que podría registrar una nueva instancia del contenedor Unity IOC y llamar a RegisterInstance en el contenedor para cada objeto simulado que pase el valor Mock.Object para cada uno. Intento que este proceso de registro sea reutilizable, así que no tengo que seguir haciendo lo mismo una y otra vez con cada prueba unitaria a menos que una prueba unitaria requiera que algunos datos específicos vuelvan del repositorio. Aquí es donde radica el problema ... ¿cuál es la mejor práctica para configurar los valores esperados en un repositorio simulado? Parece que si llamo a RegisterType en el contenedor de Unity, perdería una referencia al objeto Mock real y no podría anular el comportamiento.

Respuesta

55

Las pruebas unitarias no deben usar el contenedor en absoluto. Dependency Injection (DI) viene en dos fases:

  1. Use patrones DI para inyectar dependencias a los consumidores. No necesitas un contenedor para hacer eso.
  2. En la aplicación Composition Root, use un Contenedor DI (o DI de Poor Man) para conectar todos los componentes.

Cómo no utilizar ninguna de contenedores DI en absoluto para las pruebas unitarias

Como ejemplo, considere una clase que utiliza IRepository1. Al utilizar el patrón Constructor Injection, podemos hacer que la dependencia sea invariante de la clase.

public class SomeClass 
{ 
    private readonly IRepository1 repository; 

    public SomeClass(IRepository1 repository) 
    { 
     if (repository == null) 
     { 
      throw new ArgumentNullException("repository"); 
     } 

     this.repository = repository; 
    } 

    // More members... 
} 

en cuenta que la palabra clave readonly combinado con la Cláusula de Guardia garantiza que el campo repository no es nulo si la instancia se crea correctamente la instancia.

No necesita un contenedor para crear una nueva instancia de MyClass. Puede hacerlo directamente desde una unidad de prueba usando Moq u otra prueba doble:

[TestMethod] 
public void Test6() 
{ 
    var repStub = new Mock<IRepository1>(); 
    var sut = new SomeClass(repStub.Object); 
    // The rest of the test... 
} 

Ver here para más información ...

Cómo utilizar la Unidad para las pruebas unitarias

Sin embargo, si es absolutamente necesario utilizar Unity en sus pruebas, puede crear el contenedor y utilizar el método RegisterInstance:

[TestMethod] 
public void Test7() 
{ 
    var repMock = new Mock<IRepository1>(); 

    var container = new UnityContainer(); 
    container.RegisterInstance<IRepository1>(repMock.Object); 

    var sut = container.Resolve<SomeClass>(); 
    // The rest of the test... 
} 
+0

¡Gracias! Solo estaba viendo las cosas de la manera incorrecta. –

+0

Le rocé la respuesta y estaba pensando si debería votar abajo o no (porque parecía que estabas recomendando usar contenedor IoC en pruebas unitarias) y luego realmente leí tu respuesta ... Quería decir, aquí está mi vergonzoso +1 . Si la gente lo encuentra útil incluso hoy, debe usar ** ArgumentNullException (nameof (repository)) ** y no ** ArgumentNullException ("repository") ** - la publicación se escribió hace 5-6 años cuando esta característica no existía . – Margus

Cuestiones relacionadas