2009-12-01 89 views
23

我開始使用NHibernate,ASP.NET MVC 2.0和StructureMap開始一個新項目,並使用NUnit和Moq進行測試。對於我的每個控制器,我都有一個單獨的公共構造函數,其中注入了一個ISession。應用程序本身工作得很好,但就單元測試而言,我基本上必須模擬一個ISession來測試控制器。用Moq嘲笑NHibernate ISession

當我試圖嘲弄與MOQ的ISession的我得到了以下錯誤消息:

唯一屬性的訪問支持在中間調用

看來,我的問題是期待的名單 用戶從框架的CreateQuery方法,但谷歌搜索後,我現在更清楚的問題。

我有兩個問題:

1)這是錯誤的方式來模擬一個ISession

2)的依賴注入是有辦法修改代碼,以便它可以成功返回我的名單

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

請注意,我敢肯定,我可以創建用戶的硬編碼的列表(而不是嘲諷單個用戶並將其添加到列表中),但想我會離開這個代碼,因爲我有它馬上。

此外,此特定控制器的Index操作本質上執行了上面模仿的CreateQuery調用,以返回數據庫中的所有用戶。這是一個人爲的例子 - 不要讀任何細節。

在此先感謝您的幫助

編輯:在回答下面的評論,我加入了錯誤的堆棧跟蹤。此外,User類中的所有屬性都是虛擬的。

的TestCase 'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView' 失敗:System.NotSupportedException: 唯一屬性訪問都在一個 設置支持中間調用 。不支持的表達式 framework.CreateQuery(「from User」)。 在 Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall在 Moq.ExpressionVisitor.Visit(MethodCallExpression m)上 Moq.ExpressionVisitor.Visit(表達式 EXP)在 Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression 米)(式 EXP)處 Moq.Mock.GetInterceptor(LambdaExpression 拉姆達,模擬模擬) Moq.Mock.AutoMockPropertiesVisitor.SetupMocks(式 表達)在 Moq.Mock。 <> c__DisplayClass12 功能)在 Moq.Mock.Setup [T1,TResult](模擬模擬, 表達1 expression) at Moq.Mock 1.設置[TResult](Expression`1 表達) 控制器\ UserControllerTest。CS(29,0): 在 Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView()

+0

您能否顯示錯誤的堆棧跟蹤?用戶屬性是抽象的還是虛擬的? – 2009-12-02 00:06:01

回答

20

下面是我想到的解決方案,似乎完美的工作。再次,我不測試NHibernate,我不測試數據庫 - 我只是想測試依賴於NHibernate的控制器。初始解決方案的問題似乎是,我正在調用方法,並在MOQ設置調用中讀取會話的List成員。我通過將解決方案分解爲QueryMock和Session Mock(創建查詢返回IQuery對象)來分解這些調用。事務模擬也是必要的,因爲它是會議的依賴關係(在我的情況)......

 [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

而不是嘲諷Session,可以考慮對單元測試設置不同的Configuration。這個單元測試Configuration使用SQLite或Firebird等快速的進程內數據庫。在燈具設置中,您將完全從頭創建一個新的測試數據庫,運行腳本來設置表格並創建一組初始記錄。在每個測試設置中,您打開一個事務,並在後測試拆卸中,回滾事務以將數據庫恢復到之前的狀態。從某種意義上說,你並不是在嘲笑Session,因爲這會變得棘手,但是你正在嘲笑實際的數據庫。

+0

謝謝大法官。我當然想到了這一點,如果我無法得到這個工作,這將是我走的路。但我試圖在這個測試項目中完全避免數據庫。如果我可以模擬NHibernate,我覺得我將對我的測試擁有更多的控制權......但是謝謝你的建議! – 2009-12-02 02:25:53

+3

不幸的是,NHibernate'Session'在涉及到相關對象,延遲加載,緩存以及NHibernate所做的其他所有事情時非常複雜。所以我只是想跳過它而試圖嘲笑數據庫。 NHibernate很容易從映射中爲任何給定的數據庫系統生成一個模式創建腳本,然後執行該腳本來創建一個空的數據庫,並在架構設置中使用您的模式。從我自己的NHibernate體驗以及觀察像Rails這樣的框架來看,這是本質上唯一的方法。 – yfeldblum 2009-12-02 03:02:41

+0

嗯,你是對的......雖然它會是如此之好,我們不需要使用一些模擬分貝...如果我們已經有nhibernate它將我們的關係模型轉換爲對象模型,測試有點奇怪像這樣,我們應該可以直接測試它... 很好的問題和答案,歡呼:) – Marko 2010-07-09 11:20:03