2014-03-31 2 views
6

Durch die Nutzung der Testing with async queries section of the Testing with a Mocking Framework article on MSDN konnte ich viele erfolgreich bestandene Tests erstellen.Wie man ein Entity Framework mockt 6 Async Projecting Query

Hier ist mein Test-Code, der NSubstitute für Mocks verwendet:

var dummyQueryable = locations.AsQueryable(); 

var mock = Substitute.For<DbSet<Location>, IDbAsyncEnumerable<Location>, IQueryable<Location>>(); 
((IDbAsyncEnumerable<Location>)mock).GetAsyncEnumerator().Returns(new TestDbAsyncEnumerator<Location>(dummyQueryable.GetEnumerator())); 
((IQueryable<Location>)mock).Provider.Returns(new TestDbAsyncQueryProvider<Location>(dummyQueryable.Provider)); 
((IQueryable<Location>)mock).Expression.Returns(dummyQueryable.Expression); 
((IQueryable<Location>)mock).ElementType.Returns(dummyQueryable.ElementType); 
((IQueryable<Location>)mock).GetEnumerator().Returns(dummyQueryable.GetEnumerator()); 
sut.DataContext.Locations = mock; 

var result = await sut.Index(); 

result.Should().BeView(); 

sut.Index() nicht viel tun, aber es macht die folgende Abfrage:

await DataContext.Locations 
    .GroupBy(l => l.Area) 
    .ToListAsync()); 

Das funktioniert gut, bis ich ein hinzufügen Projektion in die Abfrage:

await DataContext.Locations 
    .GroupBy(l => l.Area) 
    .Select(l => new LocationsIndexVM{ Area = l.Key }) // added projection 
    .ToListAsync()); 

was zu diesem Ergebnis führt Ausnahme:

System.InvalidOperationException 
The source IQueryable doesn't implement IDbAsyncEnumerable<LocationsIndexVM>. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068. 
    at System.Data.Entity.QueryableExtensions.AsDbAsyncEnumerable(IQueryable`1 source) 
    at System.Data.Entity.QueryableExtensions.ToListAsync(IQueryable`1 source) 
    at Example.Web.Controllers.HomeController.<Index>d__0.MoveNext() in HomeController.cs: line 25 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 
    at Example.Test.Web.Controllers.HomeControllerShould.<TempTest>d__4.MoveNext() in HomeControllerShould.cs: line 71 

UPDATE: Ich habe uploaded a small, simple solution, die dieses Problem reproduziert.

Kann jemand ein Beispiel dafür angeben, was zum Testen einer Abfrage erforderlich ist, die async ist und eine .Select() Projektion enthält?

Antwort

15

Also habe ich ein bisschen gegraben, und das Problem ist mit der Art und Weise zu tun, wie die TestDbAsyncEnumerable<T> die IQueryProvider enthüllt. Meine beste Vermutung hinsichtlich der Argumentation ist unten und die Lösung darunter.

TestDbAsyncEnumerable<T> erbt von EnumerableQuery<T>, die wiederum von IQueryable<T> erbt, und explizit implementiert die Provider Eigenschaft dieser Schnittstelle:

IQueryProvider IQueryable.Provider { get ... } 

Da es explizit implementiert ist, gehe ich davon aus, dass die LINQ Interna explizit eine Art Guss bevor Sie versuchen, die Provider zu bekommen:

((IQueryable<T>)source).Provider.CreateQuery(...); 

ich habe keine Quelle zur Hand haben (und kann nicht gestört werden suchen nach einem), aber ich Lügen strafen Die Typ-Bindungsregeln sind für explizite Implementierungen unterschiedlich; im Wesentlichen wird die Provider Eigenschaft auf Ihrem TestDbAsyncEnumerable<T> nicht als eine Implementierung von IQueryable<T>.Provider als eine explizite eine weiter oben in der Kette, so dass Ihre TestDbAsyncQueryProvider<T> wird nie zurückgegeben.

Die Lösung für dieses ist TestDbAsyncEnumerable<T> auch IQueryable<T> erben zu machen und sich ausdrücklich Provider Eigenschaft implementieren, wie weiter unten (angepasst vom MSDN article you linked):

public class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T> 
{ 
    public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) 
    { } 

    public TestDbAsyncEnumerable(Expression expression) : base(expression) 
    { } 

    public IDbAsyncEnumerator<T> GetAsyncEnumerator() 
    { 
     return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); 
    } 

    IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() 
    { 
     return GetAsyncEnumerator(); 
    } 

    IQueryProvider IQueryable.Provider 
    { 
     get { return new TestDbAsyncQueryProvider<T>(this); } 
    } 
} 
Verwandte Themen