2009-12-01 18 views
23

Sto avviando un nuovo progetto con NHibernate, ASP.NET MVC 2.0 e StructureMap e utilizzando NUnit e Moq per il test. Per ognuno dei miei controller ho un singolo costruttore pubblico nel quale viene iniettata una ISession. L'applicazione stessa funziona bene, ma in termini di test delle unità devo essenzialmente prendere in giro un'ISession per testare i controller.Mocking di una NHibernate ISession con Moq

Quando si tenta di prendere in giro l'ISession con MOQ ottengo il seguente messaggio di errore:

Sono supportati solo accessi di proprietà nelle invocazioni intermedie

Sembra che il mio problema si aspetta Lista utenti dal metodo CreateQuery del framework ma dopo aver cercato su Google il problema ora sono più chiaro.

Ho due domande:

1) È questo il modo sbagliato di deridere l'iniezione di dipendenza di un ISession

2) c'è un modo per modificare il codice in modo che possa tornare con successo la mia lista

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

si prega di notare, io sono abbastanza sicuro che avrei potuto solo creare un elenco hardcoded di utenti (piuttosto che beffardo un singolo utente e l'aggiunta a una lista), ma ho pensato di lasciare il codice come ce l'ho proprio adesso.

Inoltre, l'azione Index di questo controller particolare esegue essenzialmente la chiamata CreateQuery imitata sopra per restituire tutti gli utenti nel database. Questo è un esempio forzato: non leggere nulla nei dettagli.

Grazie in anticipo per il vostro aiuto

Edit: In risposta al commento qui sotto, sto aggiungendo lo StackTrace per l'errore. Inoltre, tutte le proprietà della classe User sono virtuali.

TestCase 'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView' riuscita: System.NotSupportedException: Solo accessi di proprietà sono supportati in invocazioni intermedie su una configurazione . Espressione non supportata framework.CreateQuery ("from User"). a Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall (MethodCallExpression m) a Moq.ExpressionVisitor.Visit (Espressione exp) a Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall (MethodCallExpression m) a Moq.ExpressionVisitor.Visit (Expression exp) a Moq.Mock.AutoMockPropertiesVisitor.SetupMocks (Expression espressione) a Moq.Mock.GetInterceptor (LambdaExpression lambda, finto Mock) a Moq.Mock. <> c__DisplayClass12 funzione) a Moq.Mock.Setup [T1, TResult] (finto Mock, Espressione 1 expression) at Moq.Mock 1.Imp [TResult] (Expression`1 espressione) Controllori \ UserControllerTest.cs (29,0): a Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView()

+0

Potrebbe mostrare uno stacktrace dell'errore? Le proprietà dell'utente sono astratte o virtuali? –

risposta

20

Di seguito è la soluzione che ho trovato che sembra funzionare perfettamente. Ancora una volta, non sto testando NHibernate e non sto testando il database - voglio semplicemente testare i controller che dipendono da NHibernate. Il problema con la soluzione iniziale sembra essere il fatto che stavo chiamando un metodo oltre a leggere il membro di elenco della sessione nella chiamata di configurazione MOQ. Ho interrotto queste chiamate interrompendo la soluzione in QueryMock e Session Mock (la query di creazione restituisce un oggetto IQuery). Un finto dell'operazione è stato anche necessario in quanto si tratta di una dipendenza (nel mio caso) della sessione ...

 [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

Anziché beffardo Session, si potrebbe considerare la creazione di un diverso Configuration per unità di test. Questo test unitario Configuration utilizza un database veloce e in-process come SQLite o Firebird. Nella configurazione della fixture, si crea un nuovo database di test completamente nuovo, si eseguono gli script per configurare le tabelle e si crea una serie di record iniziali. Nella configurazione per test, si apre una transazione e nel post-test teardown, si esegue il rollback della transazione per ripristinare il database al suo stato precedente. In un certo senso, non stai prendendo in giro lo Session, perché questo diventa complicato, ma stai prendendo in giro il vero database.

+0

Grazie giustizia. Avevo sicuramente pensato a questo e, se non riesco a farlo funzionare, sarà il modo in cui vado. Ma sto cercando di evitare del tutto il database in questo progetto di test. Se riesco a deridere NHibernate sento che avrò molto più controllo sui miei test ... ma grazie per il suggerimento! –

+3

Sfortunatamente, il 'Session' di NHibernate è molto complicato quando si tratta di oggetti correlati, caricamento differito, memorizzazione nella cache e tutte le altre cose che fa NHibernate. Quindi vorrei solo saltare cercando di deriderlo e invece provare a prendere in giro il database. È facile per NHibernate generare uno script per la creazione di schemi per qualsiasi dato sistema di database dalle mappature e quindi per l'esecuzione di tale script per creare un database vuoto con lo schema su fixture-setup. Dalla mia esperienza con NHibernate e dall'osservazione di framework come Rails, questa è essenzialmente l'unica strada da percorrere. – yfeldblum

+0

hmm, hai ragione ... anche se sarebbe così bello che non abbiamo bisogno di usare qualche db deriso ... se abbiamo già nibernato che traduce il nostro modello relazionale in modello a oggetti, è un po 'strano da provare così, dovremmo essere in grado di testarlo direttamente ... bella domanda e risposte, evviva :) – Marko