with unit setup property objetos mocks mock c# unit-testing mocking moq

c# - setup - unit testing with mocks



Burlándose usando Moq en c# (4)

Con la forma en que ha diseñado actualmente su clase ProductBusiness , no hay manera de cambiar la implementación de IProductDataAccess utilizando un simulacro. Un patrón recomendado para esto es dependency-injection en la que se toman las dependencias de un tipo a través del constructor. Entonces tu clase se convierte en:

public class ProductBusiness { private readonly IProductDataAccess _productDataAccess; public ProductBusiness(IProductDataAccess productDataAccess) { _productDataAccess = productDataAccess; } public bool CreateProduct(Product newProduct) { bool result = _productDataAccess.CreateProduct(newProduct); return result; } }

Ahora está en condiciones de probar su clase utilizando un marco de burla como moq . Por ejemplo:

var mockDataAccess = new Mock<IProductDataAccess>(); mockDataAccess .Setup(m => m.CreateProduct(It.IsAny<Product>())) .Returns(true); var productBusiness = new ProductBusiness(mockDataAccess.Object); // ... test behaviour here

Ahora puede cambiar cómo se comporta el simulacro en su paso de configuración y asegurarse de que su método CreateProduct se comporte correctamente.

También me gustaría ver un marco de inyección de dependencia como castle-windsor . Un marco de inyección de dependencias puede resolver automáticamente dependencias, lo que significa que crear un nuevo tipo es mucho más fácil, ya que no es necesario que todo se actualice manualmente. También significa que puede cambiar qué implementación se usa en un lugar y cambia en todas partes.

Tengo el siguiente código:

public interface IProductDataAccess { bool CreateProduct(Product newProduct); }

La clase ProductDataAccess implementa esa interfaz.

public class ProductBusiness { public bool CreateProduct(Product newProduct) { IProductDataAccess pda = new ProductDataAccess(); bool result = pda.CreateProduct(newProduct); return result; } }

En este caso, ¿cómo crear una prueba unitaria para el método CreateProduct IProductDataAccess interfaz IProductDataAccess ? Pensé en tener una instancia pública de IProductDataAccess dentro de ProductBusiness e inicializarla usando el objeto Mock<IProductDataAccess> pero no es una buena práctica exponer el acceso de datos a la capa de UI. ¿Alguien puede ayudarme?


Debería inyectar la interfaz IProductDataAccess como una dependencia:

public class ProductBusiness { private IProductDataAccess _productDataAccess; public ProductBusiness(IProductDataAccess productDataAccess) { _productDataAccess = productDataAccess; } public bool CreateProduct(Product newProduct) { bool result = _productDataAccess.CreateProduct(newProduct); return result; } }

Luego puedes reemplazarlo con un simulacro en tus pruebas:

var productDataAccess = new Mock<IProductDataAccess>(); var productBusiness = new ProductBusiness(productDataAccess.Object);


Ejemplo clásico que muestra cómo, si no puede realizar una prueba unitaria de un componente en particular, ¡REFACTE el componente!

Aquí es donde el amor es lo que cualquier marco de burla te obliga a hacer: escribir código desacoplado.

En su ejemplo, la clase ProductBusiness está muy estrechamente unida a ProductDataAccess . Podría desacoplarlo usando (como la mayoría de las respuestas sugieren) inyección de dependencia. Al hacerlo, terminaría dependiendo de la abstracción de IProductDataAccess que de cualquier implementación concreta de la misma.

Otro punto a tener en cuenta, cuando está escribiendo pruebas / especificaciones para la capa empresarial, normalmente desearía probar el "comportamiento" y no el "estado". Entonces, aunque podría haber afirmaciones que verifican si se devolvió "verdadero", sus pruebas realmente deberían probar si las llamadas de acceso a datos esperadas que se establecieron con MOQ que realmente ejecutamos usando la API de "Verificación" de MOQ.

Intente agregar pruebas de comportamiento donde espera que se produzca una excepción (utilizando la API ".Throws") por la capa de acceso a datos y verifique si necesita un manejo especial en la capa empresarial.

Como sugiere Kevin, tenga el ProductBusiness que se parece a:

public class ProductBusiness { private readonly IProductDataAccess _productDataAccess; public ProductBusiness(IProductDataAccess productDataAccess) { _productDataAccess = productDataAccess; } public bool CreateProduct(Product newProduct) { bool result=_productDataAccess.CreateProduct(newProduct); return result; } }

y use cualquier marco de prueba de xunit para escribir la prueba como:

var mockDataAccess = new Mock<IProductDataAccess>(); mockDataAccess.Setup(m => m.CreateProduct(It.IsAny<Product>())).Returns(true); var productBusiness = new ProductBusiness(mockDataAccess.Object); //behavior to be tested


No debe crear una instancia de ProductDataAccess concreto dentro de su método CreateProduct . En su lugar, IProductDataAccess debe ser una dependencia inyectable . Esto se puede hacer de dos maneras:

Inyección de propiedad:

public class ProductBusiness { IProductDataAccess Pda {get; set;} } var productBusiness = new ProductBusiness(); productBusiness.Pda = new ProductDataAccess(); productBusiness.Pda = new MockProductDataAccess();

O inyección de constructor:

public class ProductBusiness { private readonly IProductDataAccess _pda; public ProductBusiness(IProductDataAccess pda) { _pda = pda; } } var productBusiness = new ProductBusiness(new ProductDataAccess()); var productBusiness = new ProductBusiness(new MockProductDataAccess());

La inyección de constructor suele ser el enfoque recomendado. La inyección de propiedad se usa para dependencias opcionales (por ejemplo, crear una instancia de un NullLogger concreto de NullLogger predeterminada en el constructor, y usar la propiedad para inyectar opcionalmente un registrador que funcione).