Shim DbContext ctor para pruebas de unidad de esfuerzo

effort entity-framework microsoft-fakes shim unit-testing

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.

public FooRepository(MyDbContext context) { _context = context; }

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

using (var context = new MyDbContext()) {
    // this way, my code isn't polluted with a ctor just for testing
}

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:

public class SomeDbContext
{
    public SomeDbContext() : base("name=Prod")
    {
    }

    // EFfort unit testing ctor
    public SomeDbContext(DbConnection connection) : base(connection, contextOwnsConnection: true) {
        Database.SetInitializer<SolrDbContext>(null);
    }
}

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

public class SomeRepository
{
    public void SomeMethodName()
    {
        using (var context = new SomeDbContext())
        {
            // self-contained in repository, no special params
            // and still calls the special test constructor
        }
    }
}

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(); 
    }
}


Related

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