2009-12-01 11 views
23

Ich beginne ein neues Projekt mit NHibernate, ASP.NET MVC 2.0 und StructureMap und mit NUnit und Moq zum Testen. Für jeden meiner Controller habe ich einen öffentlichen Konstruktor, in den eine ISession injiziert wird. Die Anwendung selbst funktioniert gut, aber in Bezug auf Komponententests muss ich eine ISession imitieren, um die Controller zu testen.Mocking ein NHibernate ISession mit Moq

Wenn ich versuche, die ISession mit MOQ zu verspotten bekomme ich folgende Fehlermeldung:

Nur Eigenschaft Zugriffe unterstützt werden in Zwischen Anrufungen

Es scheint, dass mein Problem erwartet Liste Benutzer aus dem Framework CreateQuery-Methode, aber nach dem googlen des Problems bin ich jetzt klarer.

Ich habe zwei Fragen:

1) Ist dies der falsche Weg Abhängigkeit Injektion eines ISession

2) Gibt es eine Möglichkeit zu verspotten, den Code zu ändern, damit es erfolgreich meine Liste zurückkehren kann

  [Test] 
      public void DummyTest() 
      { 

       var mock = new Mock<ISession>(); 
       var loc = new Mock<User>(); 
       loc.SetupGet(x => x.ID).Returns(2); 
       loc.SetupGet(x => x.FirstName).Returns("John"); 
       loc.SetupGet(x => x.LastName).Returns("Peterson"); 

       var lst = new List<User> {loc.Object}; 
       mock.Setup(framework => framework.CreateQuery("from User").List<User>()).Returns(lst); 

       var controller = new UsersController(mock.Object); 
       var result = controller.Index() as ViewResult; 
       Assert.IsNotNull(result.ViewData); 
      } 

Bitte beachten sie, ich ziemlich sicher bin, konnte ich nur eine hartcodierte Liste der Benutzer erstellen (und nicht einen einzelnen Benutzer spöttisch und es zu einer Liste hinzugefügt hat), aber ich dachte, den Code verlassen würde, wie ich es habe jetzt sofort.

Außerdem führt die Indexaktion dieses bestimmten Controllers im Wesentlichen den CreateQuery-Aufruf aus, der oben imitiert wurde, um alle Benutzer in der Datenbank zurückzugeben. Dies ist ein künstliches Beispiel - lesen Sie nichts in die Details.

Vielen Dank im Voraus für Ihre Hilfe

Edit: Als Antwort auf den folgenden Kommentar, Ich füge den Stacktrace für den Fehler. Außerdem sind alle Eigenschaften der Benutzerklasse virtuell.

Testcase 'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView' fehlgeschlagen: System.NotSupportedException: Nur Eigenschaft Zugriffe in Zwischen Anrufungen auf einem Setup unterstützt werden. Nicht unterstützter Ausdruck framework.CreateQuery ("from User"). bei Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall (Method m) bei Moq.ExpressionVisitor.Visit (Expression exp) bei Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall (Method m) bei Moq.ExpressionVisitor.Visit (Expression exp) bei Moq.Mock.AutoMockPropertiesVisitor.SetupMocks (Expression expression) bei Moq.Mock.GetInterceptor (Lambda lambda, Mock mock) bei Moq.Mock. <> c__DisplayClass12 Funktion) bei Moq.Mock.Setup [T1, TResult] (modells, Expression 1 expression) at Moq.Mock 1.Setup [TResult] (Expression`1 expression) Controllers \ UserControllerTest.cs (29,0): bei Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView()

+0

Könnten Sie einen Stacktrace des Fehlers zeigen? Sind die Benutzereigenschaften abstrakt oder virtuell? –

Antwort

20

Unten ist die Lösung, die ich gefunden habe, die perfekt zu funktionieren scheint. Auch hier teste ich NHibernate nicht und teste die Datenbank nicht - ich möchte einfach die Controller testen, die von NHibernate abhängig sind. Das Problem mit der anfänglichen Lösung scheint die Tatsache zu sein, dass ich eine Methode aufgerufen habe und das Listenmitglied der Sitzung im MOQ-Setup-Aufruf gelesen habe. Ich habe diese Aufrufe abgebrochen, indem ich die Lösung in einen QueryMock und einen Session Mock aufgespalten habe (create query gibt ein IQuery-Objekt zurück). Eine Transaktion Mock war auch notwendig, da es eine Abhängigkeit (in meinem Fall) von der Sitzung ...

 [Test] 
     public void DummyTest() 
     { 
      var userList = new List<User>() { new User() { ID = 2, FirstName = "John", LastName = "Peterson" } }; 
      var sessionMock = new Mock<ISession>(); 
      var queryMock = new Mock<IQuery>(); 
      var transactionMock = new Mock<ITransaction>(); 

      sessionMock.SetupGet(x => x.Transaction).Returns(transactionMock.Object); 
      sessionMock.Setup(session => session.CreateQuery("from User")).Returns(queryMock.Object); 
      queryMock.Setup(x => x.List<User>()).Returns(userList); 

      var controller = new UsersController(sessionMock.Object); 
      var result = controller.Index() as ViewResult; 
      Assert.IsNotNull(result.ViewData); 
     } 
18

Anstatt die Session spöttischen, könnte man ein unterschiedliches Configuration für Komponententests aufzubauen. Diese Unit-Test Configuration verwendet eine schnelle, in-Prozess-Datenbank wie SQLite oder Firebird. Im Fixture-Setup erstellen Sie eine neue Testdatenbank komplett neu, führen die Skripts zum Einrichten der Tabellen aus und erstellen eine Reihe von anfänglichen Datensätzen. In der Konfiguration pro Test öffnen Sie eine Transaktion, und im Nachtest-Rollback führen Sie die Transaktion zurück, um den vorherigen Zustand der Datenbank wiederherzustellen. In gewissem Sinne verspotten Sie nicht die Session, weil das schwierig wird, aber Sie verspotten die tatsächliche Datenbank.

+0

Danke Gerechtigkeit. Ich hatte definitiv darüber nachgedacht und, wenn ich das nicht funktioniere, wird es so sein wie ich gehe. Aber ich versuche, die Datenbank in diesem Testprojekt zu vermeiden. Wenn ich NHibernate verspotten kann, habe ich das Gefühl, dass ich viel mehr Kontrolle über meine Tests haben werde ... aber danke für den Vorschlag! –

+3

Leider ist die NHibernate 'Session' sehr kompliziert, wenn es um verwandte Objekte, verzögertes Laden, Zwischenspeichern und all die anderen Dinge geht, die NHibernate tut. Also würde ich einfach überspringen, um es zu verspotten und stattdessen versuchen, die Datenbank zu verspotten. Es ist einfach für NHibernate, ein Schema-Erstellungsskript für ein gegebenes Datenbanksystem aus Ihren Zuordnungen zu generieren und dieses Skript dann auszuführen, um eine leere Datenbank mit Ihrem Schema für das Fixture-Setup zu erstellen. Aus meiner eigenen Erfahrung mit NHibernate und aus der Beobachtung von Frameworks wie Rails ist dies im Wesentlichen der einzige Weg. – yfeldblum

+0

hmm, du hast Recht ... obwohl es so schön wäre, dass wir nicht irgendwelche verspotteten db benutzen müssen ... wenn wir bereits nhibernate haben, die unser relationales Modell auf das Objektmodell übertragen, ist es etwas seltsam zu testen So sollten wir in der Lage sein, es direkt zu testen ... nette Frage und Antworten, Prost :) – Marko