Wie zum Hohn ein async-repository mit Entity Framework-Core
Ich versuche zum erstellen einer unit test für eine Klasse, die Aufrufe in einer async-repository. Ich bin mit ASP.NET Kern-und Entity Framework-Kern. Mein generischen repository sieht wie folgt aus.
public class EntityRepository<TEntity> : IEntityRepository<TEntity> where TEntity : class
{
private readonly SaasDispatcherDbContext _dbContext;
private readonly DbSet<TEntity> _dbSet;
public EntityRepository(SaasDispatcherDbContext dbContext)
{
_dbContext = dbContext;
_dbSet = dbContext.Set<TEntity>();
}
public virtual IQueryable<TEntity> GetAll()
{
return _dbSet;
}
public virtual async Task<TEntity> FindByIdAsync(int id)
{
return await _dbSet.FindAsync(id);
}
public virtual IQueryable<TEntity> FindBy(Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.Where(predicate);
}
public virtual void Add(TEntity entity)
{
_dbSet.Add(entity);
}
public virtual void Delete(TEntity entity)
{
_dbSet.Remove(entity);
}
public virtual void Update(TEntity entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
}
public virtual async Task SaveChangesAsync()
{
await _dbContext.SaveChangesAsync();
}
}
Dann habe ich eine service-Klasse, die Aufrufe FindBy und FirstOrDefaultAsync auf eine Instanz des Repositorys:
public async Task<Uri> GetCompanyProductURLAsync(Guid externalCompanyID, string productCode, Guid loginToken)
{
CompanyProductUrl companyProductUrl = await _Repository.FindBy(u => u.Company.ExternalCompanyID == externalCompanyID && u.Product.Code == productCode.Trim()).FirstOrDefaultAsync();
if (companyProductUrl == null)
{
return null;
}
var builder = new UriBuilder(companyProductUrl.Url);
builder.Query = $"-s{loginToken.ToString()}";
return builder.Uri;
}
Ich versuche zu verspotten, die repository-Aufruf in meinem test unter:
[Fact]
public async Task GetCompanyProductURLAsync_ReturnsNullForInvalidCompanyProduct()
{
var companyProducts = Enumerable.Empty<CompanyProductUrl>().AsQueryable();
var mockRepository = new Mock<IEntityRepository<CompanyProductUrl>>();
mockRepository.Setup(r => r.FindBy(It.IsAny<Expression<Func<CompanyProductUrl, bool>>>())).Returns(companyProducts);
var service = new CompanyProductService(mockRepository.Object);
var result = await service.GetCompanyProductURLAsync(Guid.NewGuid(), "wot", Guid.NewGuid());
Assert.Null(result);
}
Jedoch, wenn der test führt der Aufruf der repository, bekomme ich die folgende Fehlermeldung:
The provider for the source IQueryable doesn't implement IAsyncQueryProvider. Only providers that implement IEntityQueryProvider can be used for Entity Framework asynchronous operations.
Wie kann ich richtig das mock-repository, um diese zu arbeiten?
Vielleicht kann dies helfen, msdn.microsoft.com/en-us/library/dn314429.aspx
Lesen Sie den Abschnitt auf
Sie müssen simulieren beide
Lesen Sie den Abschnitt auf
Testing with async queries
Sie müssen simulieren beide
IQueryable<T>
und IAsyncEnumerableAccessor<T>
Schnittstellen sowieInformationsquelleAutor Jed Veatch | 2016-11-07
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dank @Nkosi für den Hinweis, mich an einen link mit einem Beispiel zu tun, die gleiche Sache in EF 6: https://msdn.microsoft.com/en-us/library/dn314429.aspx. Dies hat nicht funktioniert, genau wie mit EF-Core, aber ich war in der Lage, mit ihm zu beginnen und machen Sie änderungen, um es arbeiten. Nachfolgend werden die test-Klassen, die ich geschaffen, um zu "spotten" IAsyncQueryProvider:
Und hier ist meine aktualisierte test-Fall, dass diese Klassen verwendet:
Vielen Dank für die Hilfe!
Würde ich konvertieren, um eine Erweiterung Methode, damit Sie Sie wiederverwenden können es in Ihren tests.
Überprüfen Sie die Antwort, die hier, welche Referenzen dieser Antwort, wo die extension-Methode verwendet wurde. Happy coding!!!
Nochmals vielen Dank, @Nkosi!
Funktioniert Super, vielen Dank! Vielleicht können Sie fügen Sie die folgende Erweiterung auf deine Antwort zu?
public static class IEnumerableExtensions { public static IQueryable<T> TestAsync<T>(this IEnumerable<T> source) { return new TestAsyncEnumerable<T>(source); } }
InformationsquelleAutor Jed Veatch
Versuchen, meine Moq/NSubstitute Erweiterung MockQueryable: https://github.com/romantitov/MockQueryable
unterstützt alle Sync - /Async-Operationen
DbSet auch unterstützt
Dies scheint ein wirklich schönes Paket zu verwenden, anstatt zu schreiben, eine Reihe von Doppel-Umsetzung für ein fake
IAsyncQueryProvider
etc.Die einzige Lösung, die für mich gearbeitet.
InformationsquelleAutor R.Titov
Viel weniger code-Lösung. Verwenden Sie die in-memory-db-Kontext, der sollte sich darum kümmern, bootstrapping alle sets für Sie. Sie brauchen nicht länger zu verspotten, aus dem DbSet auf Ihren Kontext, aber wenn Sie wollen, um die Daten von einem service zum Beispiel, können Sie einfach die aktuellen Daten der in-memory-Kontext.
Einfache Lösung, die scheint zu funktionieren +1
InformationsquelleAutor Dean Martin
Pflege ich zwei open-source-Projekte, die die schwere Aufgabe der Einrichtung die verhöhnt und tatsächlich emuliert
SaveChanges(Async)
.Für EF-Core: https://github.com/huysentruitw/entity-framework-core-mock
Für EF6: https://github.com/huysentruitw/entity-framework-mock
Beide Projekte haben Nuget-Pakete, die mit der integration für Moq oder NSubstitute.
InformationsquelleAutor Housy