2013-11-15 12 views
17

Googling für eine Lösung für das Problem, wie die Include-Methode auf dbset in EF6 gespottet werden. Das Problem dokumentiert ist auch hier: -Entity Framework 6 Mocking-Include-Methode auf dbset

http://entityframework.codeplex.com/discussions/461731

Leider obwohl es keine gültige Lösung in dort zu sein scheint.

Hat jemand einen Workaround gefunden?

Ich verstehe, dass wir den EF6-Kontext nicht wirklich verspotten sollten, aber der Projektleiter hat darauf bestanden.

Vielen Dank im Voraus.

+0

welches spöttische Framework? hast du "fakes framework" ausprobiert? – daryal

+0

Entschuldigung. Spott mit Rhino Mocks. –

+0

warten Sie, verspotten Sie den DbContext oder das DbSet? –

Antwort

11

Also, das ist möglich, wenn ein bisschen ein faff!

Im Folgenden habe ich den Mock-Kontext und die Sets eingerichtet und kann erfolgreich Include aufrufen. Ich denke, dass die geheime Soße darin besteht, die Aufrufe an Provider, Expression und GetEnumerator auszugeben und die DbSet-Eigenschaften für den stubbed-Kontext auf die stubbed-Sets zu setzen und den Kontext nicht zu stubben, um sie zurückzugeben.

A runnable example is available on GitHub

[Test] 
    public void CanUseIncludeWithMocks() 
    { 
     var child = new Child(); 
     var parent = new Parent(); 
     parent.Children.Add(child); 

     var parents = new List<Parent> 
      { 
       parent 
      }.AsQueryable(); 

     var children = new List<Child> 
      { 
       child 
      }.AsQueryable(); 

     var mockContext = MockRepository.GenerateStub<TestContext>(); 

     var mockParentSet = MockRepository.GenerateStub<IDbSet<Parent>>(); 
     var mockChildSet = MockRepository.GenerateStub<IDbSet<Child>>(); 

     mockParentSet.Stub(m => m.Provider).Return(parents.Provider); 
     mockParentSet.Stub(m => m.Expression).Return(parents.Expression); 
     mockParentSet.Stub(m => m.GetEnumerator()).Return(parents.GetEnumerator()); 

     mockChildSet.Stub(m => m.Provider).Return(children.Provider); 
     mockChildSet.Stub(m => m.Expression).Return(children.Expression); 
     mockChildSet.Stub(m => m.GetEnumerator()).Return(children.GetEnumerator()); 

     mockContext.Parents = mockParentSet; 
     mockContext.Children = mockChildSet; 

     mockContext.Parents.Should().HaveCount(1); 
     mockContext.Children.Should().HaveCount(1); 

     mockContext.Parents.First().Children.FirstOrDefault().Should().NotBeNull(); 

     var query = mockContext.Parents.Include(p=>p.Children).Select(pc => pc); 

     query.Should().NotBeNull().And.HaveCount(1); 
     query.First().Children.Should().NotBeEmpty().And.HaveCount(1); 

    } 
+0

Wer möchte ein Beispiel mit Moq hinzufügen? Ich habe das gleiche Problem wie hier beschrieben https://entityframework.codeplex.com/discussions/461731 – GetFuzzy

+0

@GetFuzzy das Github-Projekt, mit dem ich verlinke, hat Moq Tests in.Ich bin gerade auf einer sehr langsamen Internetverbindung im Hotel und kann nicht überprüfen, aber ich bin ziemlich sicher, dass sie laufen. –

+0

Paul, danke für den Hinweis, ich habe mir das Github-Projekt angeschaut und ja, sie funktionieren ... Allerdings probiere ich das mit EF6 und dem neuen DbSet, nicht mit IDbSet, und obwohl du es erwartest ähnlich oder gleich sein, ich habe nicht viel Glück. Ich werde weiter daran arbeiten, scheint aber machbar zu sein ... – GetFuzzy

22

hatte ich das gleiche Drama wie @GetFuzzy oben - es, dass, egal schien, was ich mich nicht die Nullreferenceexception vermeiden haben könnte, wenn ein Anruf Include() auf einem Moq DbSet gemacht wurde. Das Github-Beispiel in der anderen Antwort funktionierte leider nicht: Set.Include() gibt immer null zurück.

Nachdem ich eine Weile herumgespielt habe, habe ich mir einen Workaround ausgedacht.

[Test] 
public void CanUseIncludeWithMocks() 
{ 
    var child = new Child(); 
    var parent = new Parent(); 
    parent.Children.Add(child); 

    var parents = new List<Parent> { parent }; 
    var children = new List<Child> { child }; 

    var parentsDbSet1 = new FakeDbSet<Parent>(); 
    parentsDbSet1.SetData(parents); 

    var parentsDbSet2 = new FakeDbSet<Parent>(); 
    parentsDbSet2.SetData(parents); 

    parentsDbSet1.Setup(x => x.Include(It.IsAny<string>())).Returns(parentsDbSet2.Object); 

    // Can now test a method that does something like: context.Set<Parent>().Include("Children") etc 
} 


public class FakeDbSet<T> : Mock<DbSet<T>> where T : class 
{ 
    public void SetData(IEnumerable<T> data) 
    { 
     var mockDataQueryable = data.AsQueryable(); 

     As<IQueryable<T>>().Setup(x => x.Provider).Returns(mockDataQueryable.Provider); 
     As<IQueryable<T>>().Setup(x => x.Expression).Returns(mockDataQueryable.Expression); 
     As<IQueryable<T>>().Setup(x => x.ElementType).Returns(mockDataQueryable.ElementType); 
     As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(mockDataQueryable.GetEnumerator()); 
    } 
} 

Ich mag es nicht wirklich die Ungeschicklichkeit von zwei gefälschte DbSets zu haben, die aber aus irgendeinem Grund nicht funktioniert:

parentsDbSet1.Setup(x => x.Include(It.IsAny<string>())).Returns(parentsDbSet1.Object); 

jemand dafür eine Erklärung?

+9

'set.Setup (s => s.Include (It.IsAny ())) .Receive (set.Object);' funktioniert für mich. – Sam

+3

Ich war mir nicht sicher, aber 's.Include (It.IsAny ())' ** Funktioniert ** auch bei der Verwendung von lambdas. Kudos! – krillgar

+1

Ich füge auch die folgende Zeile als letzte Zeile in den 'FakeDbSet ' Konstruktor, so dass ich nicht wiederholen muss, dass in allen Tests: ' Setup (m => m.Include (It.IsAny ())) .Receive (Object); ' Ich habe derzeit viele Komponententests mit Moq läuft erfolgreich mit diesem Ansatz. – KarlZ

3

mit Moq-Framework funktioniert diese Methode mit allem, was ich es werfen.

public static Mock<DbSet<T>> GetMockSet<T>(this ObservableCollection<T> list) where T : class 
    { 
     var queryable = list.AsQueryable(); 
     var mockList = new Mock<DbSet<T>>(MockBehavior.Loose); 

     mockList.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider); 
     mockList.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression); 
     mockList.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType); 
     mockList.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator()); 
     mockList.Setup(m => m.Include(It.IsAny<string>())).Returns(mockList.Object); 
     mockList.Setup(m => m.Local).Returns(list); 
     mockList.Setup(m => m.Add(It.IsAny<T>())).Returns((T a) => { list.Add(a); return a; }); 
     mockList.Setup(m => m.AddRange(It.IsAny<IEnumerable<T>>())).Returns((IEnumerable<T> a) => { foreach (var item in a.ToArray()) list.Add(item); return a; }); 
     mockList.Setup(m => m.Remove(It.IsAny<T>())).Returns((T a) => { list.Remove(a); return a; }); 
     mockList.Setup(m => m.RemoveRange(It.IsAny<IEnumerable<T>>())).Returns((IEnumerable<T> a) => { foreach (var item in a.ToArray()) list.Remove(item); return a; }); 

     return mockList; 
    } 

es zu tun benutzen Sie einfach:

mockContext.Setup(p => p.<DbSetToMock>).Returns(<observableCollection to use as data>.GetMockSet().Object);` 

Dies funktioniert Rost, wenn der Kontext ein Interface implementiert, wie Sie nie mit EF, etwas zu tun haben.

EDIT:

Der Grund für die zusätzlichen Bits ist, dass wir das Ergebnis auf dem Test überprüfen können, wenn wir hinzufügen oder entfernen wir die übergebene Sammlung überprüfen und es wird das Ergebnis nach dem Test haben.

+1

Das funktionierte für mich - es vermeidet die 'NullReferenceException'. Die Sache, die ich vermisste, war, dass ich dachte, ich müsste die 'Include (Expression >) - Überladung verspotten - was nicht möglich ist, da es sich um eine Erweiterungsmethode handelt - wenn man eigentlich nur die core 'Include (string)' Methode, um Dinge zum Laufen zu bringen. – OffHeGoes

+0

Das funktioniert nicht für mich. Wenn ich die Zeile hinzufügen "mockList.Setup (m => m.Include (It.IsAny ())). Rückgabewerte (mockList.Object);", "Include" erhält eine rote Squiggly mit Fehler "CS0411 \t The type Argumente für die Methode 'EntityFrameworkQueryableExtensions.Include (IQueryable , Expression >)' kann nicht aus der Verwendung abgeleitet werden. Versuchen Sie, die Typargumente explizit anzugeben. " –

+0

@PaulGorbas Sie müssen ein Include vermissen Ich spreche aus dem Speicher, aber ich denke, es ist System.Data.Entity. –

Verwandte Themen