2017-01-26 7 views
2

Ich benutze Entity Framework und habe eine generische Repository-Methode, die mir erlaubt, eine DbSet abzufragen und auch Navigationseigenschaften. Ich versuche, einen Komponententest für Code zu schreiben, der dieses Stück Code verwendet, und ich muss es für einen Komponententest verspotten. Ich benutze Moq.Moq - Mocking eine komplexe Repository-Methode - Liste Objekt nicht zurückgegeben

Hier ist die Repository-Methode - es ist eine Methode, die mir erlaubt, mit einem Ausdruck abzufragen und auch die relevanten Navigationseigenschaften, die ich will. Ich habe dieses Muster in Julie Lermans EF im Enterprise-Kurs zu Pluralsight gesehen. Hier

public IEnumerable<TEntity> FindByInclude(Expression<Func<TEntity, bool>> predicate, 
              params Expression<Func<TEntity, object>>[] includeProperties) 
{ 
    var query = GetAllIncluding(includeProperties); 
    IEnumerable<TEntity> results = query.Where(predicate).ToList(); 
    return results; 
} 

private IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] includeProperties) 
{ 
    IQueryable<TEntity> queryable = DbSet.AsNoTracking(); 

    return includeProperties.Aggregate 
     (queryable, (current, includeProperty) => current.Include(includeProperty)); 
} 

ist ein Beispiel dafür, wie ich rufe diese Methode in meinem Code (ich bin nur den relevanten Teil des Verfahrens zeigt):

public ApiResult DeleteLocation(int id) 
{ 
    var location = _locationRepository 
     .FindByInclude(l => l.Id == id, l => l.LocationRegions, l => l.Pools) 
     .Single(); 

Also diese Abfrage würde eine Einzelzurückbringen Location Einheit von der ID, die ich in und die damit verbundenen LocationRooms und Staff Sammlungen weitergegeben haben.

Wie richte ich Moq für die FindByInclude Methode ein? Hier ist, was ich für meinen Unit-Test-Mock-Setup:

var mockLocationRepository = new Mock<ILocationRepository>(); 
var location = new Location {Id = 1,Name = "LocationName", LocationRooms = new List<LocationRoom>(), Staff = new List<Staff>()}; 
mockLocationRepository.Setup(r => r.FindByInclude(l => l.Id == It.IsAny<int>(), l => l.LocationRooms, l => l.Staff)) 
      .Returns(() => new List<Location> { location }); 

Vom Moq Setup-Code hier gezeigt - ich denke, ich soll eine Liste von 1 Ort werde immer wieder - der Standort Objekt, das ich mit Id von 1 angegeben. Wenn ich jedoch meinen Komponententest ausführen und diesen Code ankreuzen, gibt die Setup-Methode für FindByInclude eine leere Liste zurück. Und deshalb, wenn der Code in der DeleteLocation Methode getroffen wird und die Single() Methode aufgerufen wird, bekomme ich einen Fehler, dass das "Element keine Sequenz enthält".

Ich denke, das Problem ist, ich habe etwas falsch mit der Syntax mit dem Moq-Setup für die FindByInclude-Methode, aber nicht sicher, was falsch ist.

Antwort

2

zu lang für einen Kommentar so Zugabe als Antwort

es die Ausdrücke ist. Probieren Sie zuerst einen allgemeineren Ausdruck aus und sehen Sie, ob es funktioniert.

var location = new Location { 
    Id = 1, 
    Name = "LocationName", 
    LocationRooms = new List<LocationRoom>(), 
    Staff = new List<Staff>() 
}; 
mockLocationRepository 
    .Setup(m => m.FindByInclude(It.IsAny<Expression<Func<TEntity, bool>>>(), It.IsAny<Expression<Func<TEntity, object>>[]>()) 
    .Returns(() => new List<Location> { location }); 
+0

Dank Nkosi - das ist genau was ich brauchte :) – user1750537

4

Als Alternative zu @ Nkosi Antwort, wie über Sie verwenden Moq nicht aber sich selbst eine Stub-Implementierung von ILocationRepository implementieren? Die Idee dahinter ist, dass, wenn Spott schwierig wird, sollten Sie es vielleicht nicht tun?

public class StubLocationRepository : ILocationRepository 
{ 
    private readonly IEnumerable<Location> _findByInclude; 

    public StubLocationRepository(IEnumerable<Location> findByInclude) 
    { 
     _findByInclude = findByInclude; 
    } 

    public IEnumerable<Location> FindByInclude(
     Expression<Func<Location, bool>> predicate, 
     params Expression<Func<Location, object>>[] includeProperties) 
    { 
     return _findByInclude; 
    } 
} 

Das ist simpel, weil es davon ausgeht, dass Sie nur eine Methode haben. Wenn Sie eine Menge haben und keine konstanten Werte für jede von ihnen übergeben möchten, können Sie die ctor des Stubs optionale Parameter verwenden, so dass Sie nur die gewünschten Methoden stub.

Da auch ILocationRepository höchstwahrscheinlich von einer generischen Schnittstelle erbt, können Sie eine generische Stub-Implementierung, die Sie spezifische Stubs bauen Unterklasse - das heißt, die Methoden zu implementieren, die ILocationRepository definiert.

+0

Toller Vorschlag - vielen Dank - Ich werde mich daran erinnern für zukünftige Zeiten, wenn ich stecken bleibe - Nkosis Antwort als richtig markieren, da es schneller zu implementieren war - ich hatte nur eine Syntax Fehler. Prost – user1750537

+1

Ich mag diese Idee auch und stimme dem Schwierigkeitsmantra zu. – Nkosi

+0

@Nkosi Danke, behoben. –

Verwandte Themen