Sto lavorando su un'API di ASP.Net Core 2.0 che utilizza Entity Framework Core 2.0. Sto cercando di creare test unitari usando XUnit e Moq ma sto incontrando un problema con la creazione di un'interfaccia per il mio DbContext
modo da poter DbContext
nei miei test di unità.
Attualmente, il mio progetto non sta utilizzando un'interfaccia per il mio contesto. Lo sto iniettando nelle mie classi di repository come sua implementazione. E nel mio Startup.cs
sto usando services.AddDbContext
per configurarlo.
Esempio di un tipico costruttore di classi repository.
public CompaniesRepository(MyDbContext myDbContext)
{
_myDbContext = myDbContext;
}
Esempio di Startup.cs
services.AddDbContext<MyDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("MyDbConnectionString"),
sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(5,TimeSpan.FromSeconds(30),sqlTransientErrors);
});
});
E questo metodo funziona così bene.
Tuttavia, ora sto provando a impostare i test unitari e voglio essere in grado di simulare il mio contesto, quindi ho bisogno di creare un'interfaccia per questo.
Così, ho aggiunto un'interfaccia a MyDbContext
chiamata IMyDbContext
e aggiunto il seguente codice al mio Startup.cs
, seguendo i consigli in questo post del blog di Jerrie Pelser
services.AddScoped<IMyDbContext>(provider => provider.GetService<MyDbContext>());
Che sembrava funzionare, tranne che per un problema. Sto anche usando Boris Djurdjevic's EFCore.BulkExtensions
NuGet e quindi, sto ricevendo un errore di compilazione proveniente dalle mie classi di repository, che ora iniettano l'interfaccia IMyDbContext
nei loro costruttori, affermando che la mia interfaccia non contiene una definizione per BulkInsert
:
Errore CS1929 'IMyDbContext' non contiene una definizione per 'BulkInsert' e il migliore metodo di estensione overload 'DbContextBulkExtensions.BulkInsert (DbContext, IList, BulkConfig, Action)' richiede un destinatario di tipo 'DbContext'
Presumo che ho bisogno di aggiungere il metodo di estensione BulkInsert
alla mia interfaccia IMyDyBontext
qualche modo, ma non sono sicuro di come farlo correttamente. Se provo ad aggiungere il metodo alla mia interfaccia, ricevo un errore che dice che non è implementato nella mia classe MyDbContext
, ovviamente.
Come faccio a fare riferimento al metodo di estensione BulkInsert
nella mia classe MyDbContext
?
BulkInsert
è un metodo di estensione. Non è possibile simulare facilmente il metodo statico.
Invece, potresti implementarlo nella classe MyDbContext
. Quindi, potresti prendere in giro IMyDbContext
per il test delle unità. Nota: non l'ho provato
public interface IMyDbContext
{
void BulkInsert<T>(IList<T> entities, BulkConfig bulkConfig = null,
Action<decimal> progress = null) where T : class;
}
public class MyDbContext : DbContext, IMyDbContext
{
public void BulkInsert<T>(IList<T> entities, BulkConfig bulkConfig = null,
Action<decimal> progress = null) where T : class
{
this.BulkInsertOrUpdate(entities, bulkConfig, progress);
}
}
Di recente ho riscontrato questo problema mentre mi preparavo a deridere il mio DbContext per i test unitari durante l'utilizzo del pacchetto EF Core BulkExtensions. La risposta contrassegnata come risposta corretta è QUASI corretta (anche se hanno detto di non aver testato il loro codice).
Quando si implementa questo all'interno della classe DbContext nel modo sopra descritto, si riceverà StackOverflowException quando si chiama il metodo BulkInsert.
Quindi, ho iniziato il debug e ho deciso di mettere un breakpoint sul metodo all'interno della classe DbContext per vedere cosa stava succedendo. Questo mi ha portato a capire il motivo di StackOverflowException: questo metodo si chiamava da solo invece del metodo sulla classe DbContext di base a causa dell'ambito della parola chiave "this".
La soluzione era abbastanza semplice, risolta dalla seguente modifica all'implementazione del metodo BulkInsert all'interno dell'implementazione MyDbContext:
public async Task BulkInsertAsync<T>(IList<T> entities, BulkConfig bulkConfig = null, Action<decimal> progress = null) where T : class
{
await ((DbContext)this).BulkInsertAsync<T>(entities, bulkConfig, progress);
}
Inoltre, aggiungi la seguente riga all'interfaccia IMyDbContext:
Task BulkInsertAsync<T>(IList<T> entities, BulkConfig bulkConfig = null, Action<decimal> progress = null) where T : class;
Ciò ha assicurato che l'ambito di "this" è stato trasmesso al tipo di classe DbContext di base anziché al tipo della mia implementazione DbContext. Pertanto, invece di chiamare se stesso, ha chiamato il metodo di estensione utilizzando la classe DbContext di base.
Utilizzando questa soluzione, ora dovresti essere in grado di utilizzare correttamente il metodo di estensione nel codice come mostrato di seguito:
await _context.BulkInsertAsync<User>(users);
Spero che qualcuno lo trovi utile. Non è stato poi così difficile capire attraverso il debug e mi sono assicurato di poter tenere tutti i miei metodi facilmente testabili in unità quando si utilizza il pacchetto BulkExtensions!