2014-05-04 12 views
15

Próbuję wyśmiać DbContext i DbSet. Działa to dla moich poprzednich testów jednostkowych, ale problem występuje, gdy mój kod wywoływał metodę ToList na DbSet drugi raz.DbSet mock, brak wyników podczas wywoływania ToList po drugie

Pierwsza dbSet.ToList() zwraca fałszywe wyniki. Drugi zwraca 0 elementów;

 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); 

Odpowiedz

36

Zwraca się tę samą instancję wyliczającą przy każdym połączeniu z numerem GetEnumerator. Kiedy wylicza jeden raz, to jest zrobione, EF nie nazywa swojej metody Reset, raczej prosi o nowy moduł wyliczający.

Ale zwróci się ten, który właśnie poddał wszystkie elementy i nie daje już więcej.

Zamiast tego zwróć funkcję, która zwraca moduł wyliczający, który zwróci nowy moduł wyliczający za każdym razem, gdy o to poprosisz.

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

To mnie zabijało. Ładny chwyt. – RMD

+0

To był dla mnie niedźwiedź od kilku miesięcy. W końcu znalazłem się w sytuacji, w której nie mogłem tego uniknąć. To bardzo pomogło! –

+0

To właśnie uczyniło moje życie o wiele lepszym. Dziękuję Ci. –

0

Jeśli umieścisz klauzulę "Where" przed wywołaniem .ToList(), dane powinny pozostać obecne.

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

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

Chciałam tylko dodać do odpowiedzi Wiktora Zychli moją małą część. Jeśli ktoś szuka Async wersji tego mock (pochodzące z tego poradnika: http://msdn.microsoft.com/en-us/data/dn314429.aspx#async) to jest moja modyfikacja TestDbAsyncEnumerator<T> Klasa:

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; } 
    } 
} 

Potem jak Wiktor zasugerował trzeba skonfigurować go z delegatem tak w przypadku async byłoby tak:

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

Jeśli ktoś chce za źródło, które następnie udać się tutaj: https://github.com/kelostrada/EntityFrameworkWithMock.Test

+0

Kod w repozytorium GitHub nie pasuje do tego, co masz w stackoverflow? Nie działa w moim przypadku w żaden sposób. – Riga

+0

co nie działa dla ciebie?Nie oglądałem tego kodu przez ponad 2 lata, może nie działać z powodu niektórych aktualizacji pakietów i podobnych rzeczy. Czy to się nie kompiluje? Poza tym odkryłem, że próba sfałszowania DbSets była jak najtrudniejsze zadanie, z jakim się kiedykolwiek spotkałem ... nigdy więcej tego nie spróbuję. Wystarczy skonfigurować nową bazę danych do testów i ustawić transakcję dla każdego testu - o wiele bardziej niezawodne i łatwe w użyciu rozwiązanie. (może tylko odrobinę wolniej) –

+0

mój komentarz dotyczy raczej kodu tutaj, a połączone repozytorium nie pasuje. Twój kod kompiluje, ale nie rozwiązuje podobnego problemu opisanego tutaj: http://stackoverflow.com/questions/41899177/mock-entity-framework-long-linq-query/41900708#41900708. Zrozumcie teraz, że szyderstwo DbSets jest bardzo trudną drogą, a może nie najlepszą, uważamy, że EF był testowalny, ale być może nie. Teraz już za późno, aby zmienić rzeczy w pracy nad projektem. Dzięki. – Riga

2

nie mogę wypowiedzieć się na temat postu Wiktora jak nie mam wystarczająco dużo reputacji, ale chciałbym lubię dodać trochę więcej do odpowiedzi Wiktora.

Kod

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

Czy powiedzie się, jeśli dasz mu lambda zerowego argumentu jak w przykładzie (przynajmniej w wersji używam). Podanie argumentu, który nigdy nie jest używany, pozwoli spełnić wymóg podpisu.

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

Kompiluje się jednak i działa zgodnie z oczekiwaniami.

I dotyczy to również odpowiedź Kelu, jak również (chociaż on dodał lambda w niewłaściwym miejscu.

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

Nie stara się przyczepić odpowiedzi tutaj, jestem po prostu dodając bo zrobiłem te błędy się :)

Powiązane problemy