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