Shim DbContext ctor per il test dell'unità di sforzo

effort entity-framework microsoft-fakes shim unit-testing

Domanda

Mi piacerebbe intercettare var context = new MyDbContext() per restituire invece una chiamata al costruttore differente.

Il bello di EFfort è che ti permette di creare un semplice database in memoria per il test delle unità.

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

Ma poi dovresti iniettare quel context nel tuo repository.

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

È possibile intercettare var context = new MyDbContext() , in modo che restituisca il testContext ?

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

Risposta accettata

(Modifica: ho appena realizzato che in realtà non sta restituendo l'altra chiamata del ctor.

Capito. Abbastanza semplice se sai come farlo:

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

            // Act


            // Assert

        }

E come al solito, EFfort richiede questo costruttore nella classe 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);
    }
}

Ma significa che il repository è beatamente inconsapevole della speciale connessione Transient:

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

Risposta popolare

Hai due opzioni possibili. Usando le fabbriche o tramite la programmazione orientata all'aspetto (come PostSharp)

facendo riferimento a questo articolo: http://www.progware.org/Blog/post/Interception-and-Interceptors-in-C-(Aspect-orientated-programming).aspx

Utilizzo di PostSharp (AOP)

PostSharp è un ottimo strumento e può raggiungere l'intercettazione più pulita possibile (ovvero non comporta alcuna modifica nelle classi e nella generazione di oggetti, anche se non sono le tue fabbriche per la creazione e / o l'interfaccia di oggetti) ma non è una libreria libera. Piuttosto che creare proxy in fase di runtime, esso inietta il codice in fase di compilazione e pertanto modifica il programma iniziale in modo semplice per aggiungere l'intercettazione del metodo.
.....
La cosa bella in questo è che non cambierai nient'altro nel tuo codice, quindi il tuo oggetto può ancora essere generato usando la nuova parola chiave.

Utilizzando DI e modello di fabbrica

Personalmente preferisco l'approccio del modello di fabbrica, ma sembra che tu abbia bisogno di iniettare dipendenze nelle tue classi.

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

Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow