Shim DbContext ctor para pruebas de unidad de esfuerzo


Pregunta

Me gustaría interceptar var context = new MyDbContext() para devolver una llamada de constructor diferente.

Lo mejor de EFfort es que le permite configurar una base de datos en memoria fácil para la prueba de unidades.

var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);

Pero entonces tendrías que inyectar ese context en tu repositorio.

var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);

¿Es posible simplemente interceptar var context = new MyDbContext() , para que devuelva el testContext ?

var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);

Respuesta aceptada

(Editar: Me acabo de dar cuenta de que esto no es en realidad devolver la llamada del otro ctor. Trabajando en ello).

Lo averigué. Bastante simple si sabes cómo hacerlo:

        [TestMethod]
        public void Should_have_a_name_like_this()
        {
            // Arrange
            var connection = Effort.DbConnectionFactory.CreateTransient();
            ShimSolrDbContext.Constructor = context => new SolrDbContext(connection);

            // Act


            // Assert

        }

Y como es habitual, EFfort requiere este constructor en la clase DbContext:

        [TestMethod]
        public void Should_have_a_name_like_this()
        {
            // Arrange
            var connection = Effort.DbConnectionFactory.CreateTransient();
            ShimSolrDbContext.Constructor = context => new SolrDbContext(connection);

            // Act


            // Assert

        }

Pero significa que el repositorio es felizmente inconsciente de la conexión Transiente especial:

        [TestMethod]
        public void Should_have_a_name_like_this()
        {
            // Arrange
            var connection = Effort.DbConnectionFactory.CreateTransient();
            ShimSolrDbContext.Constructor = context => new SolrDbContext(connection);

            // Act


            // Assert

        }

Respuesta popular

Tienes dos opciones posibles. Usando fábricas o mediante la programación orientada a Aspectos (como PostSharp)

haciendo referencia a este artículo: http://www.progware.org/Blog/post/Interception-and-Interceptors-in-C-(Aspect-oriented-programming).aspx

Utilizando PostSharp (AOP)

PostSharp es una gran herramienta y puede lograr la intercepción más limpia posible (lo que significa que no hay cambios en sus clases y generación de objetos, incluso si no hace sus fábricas para la creación de objetos y / o interfaces) pero no es una biblioteca gratuita. En lugar de crear proxies en tiempo de ejecución, inyecta código en tiempo de compilación y, por lo tanto, cambia su programa inicial de manera transparente para agregar la intercepción de métodos.
.....
Lo bueno de esto es que no cambia nada más en su código, por lo que su objeto aún puede generarse usando la nueva palabra clave.

Usando DI y patrón de fábrica

Personalmente prefiero el enfoque de patrón de fábrica, pero parece estar dispuesto a tener que inyectar cualquier dependencia en sus clases.

public interface IDbContextFactory<T> where T : DbContext {
    T Create();
}

public class TestDbContextFactory : IDbContextFactory<MyDbContext> {
    public MyDbContext Create() {
        var connection = Effort.DbConnectionFactory.CreateTransient();
        var testContext = new MyDbContext(connection);
        return testContext;
    }
}

public class FooRepository {
    MyDbContext _context;
    public FooRepository(IDbContextFactory<MyDbContext> factory) { 
        _context = factory.Create(); 
    }
}




Licencia bajo: CC-BY-SA
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué