2014-05-04 4 views
15

Ich versuche DbContext und DbSet zu verspotten. Dies funktioniert für meine vorherigen Komponententests, aber das Problem tritt auf, wenn mein Code die ToList-Methode zum zweiten Mal auf DbSet aufruft.DbSet Mock, keine Ergebnisse beim zweiten Aufruf von ToList

Erste dbSet.ToList() gibt mocked Ergebnisse zurück. Zweite gibt 0 Elemente zurück;

 var queryableData = new List<string>{ "a", "b", "c" }.AsQueryable(); 

     var mockDbSet = new Mock<DbSet<string>>(); 
     var q = mockDbSet.As<IQueryable<string>>(); 
     q.Setup(m => m.Provider).Returns(queryableData.Provider); 
     q.Setup(m => m.Expression).Returns(queryableData.Expression); 
     q.Setup(m => m.ElementType).Returns(queryableData.ElementType); 
     q.Setup(m => m.GetEnumerator()).Returns(queryableData.GetEnumerator()); 

     DbSet<string> dbset = mockDbSet.Object; 
     IQueryable<string> query = dbset; 

     //RESULTS: abc 
     var a1 = dbset.ToList(); 
     foreach (var a in a1) 
      Console.Write(a); 

     //NO RESULTS 
     var a2 = dbset.ToList(); 
     foreach (var a in a2) 
      Console.Write(a); 

Antwort

36

Sie kehren die gleiche enumerator Instanz bei jedem Aufruf an GetEnumerator. Wenn es einmal aufzählt, ist es getan, EF ruft nicht seine Reset Methode auf, sondern fragt nach einem neuen Enumerator.

Aber Sie geben das zurück, das nur alle Elemente ergeben hat und nicht mehr ergibt.

Geben Sie stattdessen eine Funktion zurück, die den Enumerator zurückgibt, der jedes Mal, wenn Sie danach gefragt werden, einen neuen Enumerator zurückgibt.

q.Setup(m => m.GetEnumerator()).Returns(() => queryableData.GetEnumerator()); 
+0

Das hat mich umgebracht. Großer Fang. – RMD

+0

Das war für ein paar Monate ein Bär für mich. Ich kam schließlich in eine Situation, in der ich es nicht vermeiden konnte. Das hat sehr geholfen! –

+0

Das hat mein Leben so viel besser gemacht. Vielen Dank. –

0

Wenn Sie vor dem Aufruf von .ToList() eine "Where" -Klausel setzen, sollten die Daten vorhanden bleiben.

var a1 = dbset.Where(m => m != null).ToList(); 

var a2 = dbset.Where(m => m != null).ToList(); 
2

Ich wollte nur Wiktor Zychlas Antwort mein kleiner Teil hinzugefügt werden. Wenn jemand für Async-Version dieses mock (aus diesem Tutorial: http://msdn.microsoft.com/en-us/data/dn314429.aspx#async) sucht, dann ist dies meine Änderung TestDbAsyncEnumerator<T> Klasse:

internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> 
{ 
    private readonly IEnumerator<T> _inner; 

    public TestDbAsyncEnumerator(IEnumerator<T> inner) 
    { 
     _inner = inner; 
    } 

    public TestDbAsyncEnumerator(Func<IEnumerator<T>> valueFunction) 
    { 
     _inner = valueFunction(); 
    } 

    public void Dispose() 
    { 
     _inner.Dispose(); 
    } 

    public Task<bool> MoveNextAsync(CancellationToken cancellationToken) 
    { 
     return Task.FromResult(_inner.MoveNext()); 
    } 

    public T Current 
    { 
     get { return _inner.Current; } 
    } 

    object IDbAsyncEnumerator.Current 
    { 
     get { return Current; } 
    } 
} 

Dann wie Wiktor vorgeschlagen, dass Sie Setup haben es mit Delegierten so bei Asynchron es so wäre, denn das

mockSet.As<IDbAsyncEnumerable<Blog>>() 
      .Setup(m => m.GetAsyncEnumerator()) 
      .Returns(new TestDbAsyncEnumerator<Blog>(() => data.GetEnumerator())); 

Wenn jemand will, Quelle hier dann gehen Sie: https://github.com/kelostrada/EntityFrameworkWithMock.Test

+0

Der Code in Ihrem GitHub Repo entspricht nicht dem, was Sie in Stackoverflow haben? Funktioniert bei mir nicht so oder so. – Riga

+0

was funktioniert nicht für dich?Ich habe diesen Code seit mehr als 2 Jahren nicht mehr gesehen, es könnte wegen einiger Paket-Updates und solchen Sachen nicht funktionieren. Kompiliert es nicht? Außerdem fand ich, dass der Versuch, DbSets zu verspotten, die schwierigste Aufgabe war, die ich jemals antrat ... würde das nie wieder versuchen. Richten Sie einfach eine neue Datenbank für Tests ein und legen Sie für jeden Test eine Transaktion fest - eine viel zuverlässigere und benutzerfreundlichere Lösung. (vielleicht nur ein bisschen langsamer) –

+0

mein Kommentar ist hier mehr über Code und in dem Repository, das Sie verlinkt nicht übereinstimmen. Ihr Code erstellt, aber nicht ähnliche Problem behoben haben hier beschrieben: http://stackoverflow.com/questions/41899177/mock-entity-framework-long-linq-query/41900708#41900708. Verstehe jetzt, dass es ein sehr schwieriger Weg ist, DbSets zu verspotten und vielleicht nicht das Beste, dachte EF wurde testbar gemacht, aber vielleicht auch nicht. Zu spät, um die Dinge im Projekt zu ändern. Vielen Dank. – Riga

2

ich nicht auf Wiktor der Beitrag kommentieren, da ich nicht genug Ruf bekommen haben, aber ich würde Ich möchte ein wenig mehr zu Wiktors Antwort hinzufügen.

Der Code

mockSet.Setup((m => m.GetEnumerator()).Returns(() => data.GetEnumerator()) 

schlagen fehl, wenn Sie es ein Null-Argument Lambda wie im Beispiel geben (zumindest die Version ich verwende). Wenn Sie ein Argument übergeben, das niemals verwendet wird, kann es die Signaturanforderung erfüllen.

mockSet.Setup((m => m.GetEnumerator()).Returns(x => data.GetEnumerator()) 

Wird jedoch kompiliert und funktioniert wie erwartet.

Und dies gilt auch für Kelu Antwort als auch (obwohl er das Lambda an der falschen Stelle hinzugefügt.

mockSet.As<IDbAsyncEnumerable<Blog>>() 
     .Setup(m => m.GetAsyncEnumerator()) 
     .Returns(x => new TestDbAsyncEnumerator<Blog>(data.GetEnumerator())); 

Nicht Antworten versuchen, hier zu kleinlich, ich bin einfach nur mit, weil ich diese Fehler selbst gemacht habe :)

Verwandte Themen