2013-04-25 15 views
8

Ich versuche Moq zu verwenden, um einige Tests für Entity Framework-Code Erste Klassen zu machen. Ich bin sehr neu in Moq und spöttische Techniken und ich frage mich, ob es leicht ist, einen Test zu machen, den ich unten beschreiben werde. Ich suchte im Internet nach Lösungen, aber die meisten basieren auf Repository-Mustern, die ich vermeiden möchte.Wie Moq mit Entity Framework IDbSet Count() verwenden usw. Methoden

Ich habe ITestEntities Schnittstelle für Kontext

public interface ITestEntities 
{ 
    IDbSet<Order> Orders { get; } 
    IDbSet<Product> Products { get; } 
    IDbSet<User> Users { get; } 
} 

Dann Kontext

public class TestEntities : DbContext, ITestEntities 
{ 
    public TestEntities() : base("name=TestEntities") 
    { 

    } 

    public virtual IDbSet<Order> Orders { get; set; } 
    public virtual IDbSet<Product> Products { get; set; } 
    public virtual IDbSet<User> Users { get; set; } 
} 

Ein Controller und eine Aktion

public class HomeController : Controller 
{ 
    private ITestEntities db; 

    public HomeController() 
    { 
     db = new TestEntities(); 
    } 

    public HomeController(ITestEntities db) 
    { 
     this.db = db; 
    } 

    public ActionResult Index() 
    { 
     var count = db.Users.Count(); 
     ViewBag.count = count; 

     return View(count); 
    }   
} 

zu testen und schließlich ein NUnit Test mit Moq

[Test] 
public void ModelValueShouldBeTwo() 
{ 
    var mockUsers = new Mock<IDbSet<User>>(); 
    mockUsers.Setup(m => m.Count()).Returns(2); 

    var mockDB = new Mock<ITestEntities>(); 
    mockDB.Setup(db => db.Users).Returns((IDbSet<User>)mockUsers); 

    var controller = new HomeController((ITestEntities)mockDB); 

    var view = controller.Index(); 

    Assert.IsInstanceOf<ViewResult>(view); 
    Assert.AreEqual(((ViewResult)view).Model, 2); 
} 

Das Problem ist, mit dieser Zeile: mockUsers.Setup(m => m.Count()).Returns(2);. Wenn Sie diesen Test ausführen bekomme ich Fehler folgende:

System.NotSupportedException : Expression references a method that does not belong to the mocked object: m => m.Count<User>()

Ich denke, dies zu .Count() aufgrund ist eine statische Methode sein, damit es nicht von Moq verspottet werden kann. Gibt es eine Möglichkeit, diese einfache Aktion mit Moq zu testen und nicht vollwertiger Repository-Muster verwenden, die als ich ohnehin diesen .Count() Teil in irgendeine Methode fest einprogrammiert habe verstehen sollte überprüfbar zu sein ... Vielleicht benutze ich nur die Mocks in einer falschen Weise ? Denn ich habe den Eindruck, dass dies mit EF Code First ganz einfach und möglich sein sollte.

Antwort

14

Wenn Sie die Testeinheiten spotten Sie brauchen nicht zu verspotten weiter unten in der Kette

etwas tun sollte (obwohl, ich bin nicht an einer IDE so können einige Optimierungen benötigen)

aktualisieren neue InMemoryDbSet

[Test] 
public void ModelValueShouldBeTwo() 
{ 
    //Build test users 
    var mockUsers = new InMemoryDbSet<User>(){ new User(), new User()}; 
    var mockDB = new Mock<ITestEntities>(); 
    //Set up mock entities to returntest users. 
    mockDB.Setup(db => db.Users).Returns(mockUsers); 

    var controller = new HomeController((ITestEntities)mockDB); 

    var view = controller.Index(); 

    Assert.IsInstanceOf<ViewResult>(view); 
    Assert.AreEqual(((ViewResult)view).Model, 2); 
} 

aufzunehmen Damit werden die Erweiterungsmethoden bedeuten wird einfach aus der Testdaten, die Sie geliefert haben, arbeiten.

unten für einen guten Artikel finden Sie auf spöttische dbset http://geekswithblogs.net/Aligned/archive/2012/12/12/mocking-or-faking-dbset.aspx

+0

Hinweis: Dies funktioniert nicht für Async-Abfragen. Eine Lösung dafür finden Sie in den offiziellen EF-Dokumenten: http://msdn.microsoft.com/en-us/data/dn314429.aspx#async –

1

Mock GetEnumerator() statt Count()

Count() ist eine Erweiterungsmethode auf Objekte, die IEnumerable<T> implementieren und IDbSet<T> implementiert IEnumerable<T>

Erweiterung Methoden übergeben das Objekt, an dem sie aufgerufen werden. In diesem Fall ist die Unterschrift:

public static int Count<TSource>(
    this IEnumerable<TSource> source, //This is your IDbSet that you are mocking 
    Func<TSource, bool> predicate 
) 

Anstatt Count() Setup versucht, einen bestimmten Wert zurückzukehren, können Sie Setup-Mitglieder IEnumerable<T> das gleiche Ergebnis zu erzielen. Im Fall von IEnumerable<T> müssen Sie nur GetEnumerator() einrichten, um eine Enumerator<T> zurückzugeben, die über zwei Werte aufzählt.Ich schaffe

In dieser Situation in der Regel, dass Enumerator<T> durch eine neue Liste mit ein paar Elemente erstellen und ruft GetEnumerator() darauf:

mockUsers.Setup(m => m.GetEnumerator()).Returns(new List<Users> { 
    new User(), 
    new User() 
}.GetEnumerator()); 

Nun, natürlich dies effektiv testet die Erweiterungsmethode Count() zusätzlich zu was auch immer Sie versuchen, mit Ihrem Test zu erreichen, während das ein ziemlich geringes Risiko ist, wenn die Erweiterungsmethode ein Teil von .NET ist, ist es etwas, das Sie beachten sollten, wenn Sie eigene Erweiterungsmethoden verwenden und entwickeln.

+1

Dies funktioniert für einige Arten von Abfragen gegen ein 'IDbSet', aber nicht für alle Zum Beispiel 'FirstOrDefault()', das das Enumerable nicht sonderlich nutzt. –

Verwandte Themen