Shim DbContext ctor für das Testen von Testeinheiten

effort entity-framework microsoft-fakes shim unit-testing

Frage

Ich möchte var context = new MyDbContext() abfangen, um stattdessen einen anderen Konstruktoraufruf zurückzugeben.

Das Schöne an EFfort ist, dass Sie eine einfache In-Memory-Datenbank für Komponententests einrichten können.

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

Aber dann müssten Sie diesen context in Ihr Repository einfügen.

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

Ist es möglich, var context = new MyDbContext() einfach abzufangen, so dass es den testContext ?

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

Akzeptierte Antwort

(Bearbeiten: Ich habe gerade gemerkt, dass dies nicht wirklich den anderen ctor-Aufruf zurückgibt. Arbeiten daran.)

Herausgefunden. Einfach genug, wenn Sie wissen, wie es geht:

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

            // Act


            // Assert

        }

Und wie üblich benötigt EFfort diesen Konstruktor in der DbContext-Klasse:

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

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

Aber es bedeutet, dass der Repo die spezielle transiente Verbindung glücklicherweise nicht kennt:

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
        }
    }
}

Beliebte Antwort

Sie haben zwei mögliche Optionen. Verwendung von Fabriken oder über aspektorientierte Programmierung (wie PostSharp)

Diesen Artikel referenzierend: http://www.progware.org/Blog/post/Interception-and-Interceptors-in-C-(Aspect-oriented-programming).aspx

Verwenden von PostSharp (AOP)

PostSharp ist ein großartiges Werkzeug und kann das sauberste Abfangen erreichen, das möglich ist (das bedeutet keine Änderungen in Ihren Klassen und der Objekterzeugung, auch wenn Sie nicht Ihre Fabriken für Objekterstellung und / oder Schnittstellen haben), aber es ist keine freie Bibliothek. Anstatt Proxies zur Laufzeit zu erstellen, injiziert es Code zur Kompilierungszeit und ändert daher Ihr initiales Programm auf nahtlose Weise, um Methodenabfangen hinzuzufügen.
.....
Das coole daran ist, dass Sie nichts anderes in Ihrem Code ändern, sodass Ihr Objekt immer noch mit dem neuen Schlüsselwort generiert werden kann.

DI und Factory-Muster verwenden

Ich persönlich bevorzuge den Factory-Pattern-Ansatz, aber Sie scheinen alle Abhängigkeiten in Ihre Klassen einfügen zu müssen.

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

Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow