2014-06-17 49 views
2

背景: - 使用的EntityFramework 6 - 使用Moq的v4.2.1402.2112 - 使用DbFirst方法實體框架6和Moq4:是否可以讓模擬DbSet在其範圍內保留添加的數據?

我已經繼EF6 Moq的漫遊(可發現here)然而;我一直在想,是否有可能讓模擬的DbSet保留添加到其範圍內的數據?

例如:

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Moq; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 

namespace TestingDemo 
{ 
    [TestClass] 
    public class QueryTests 
    { 
     [TestMethod] 
     public void GetAllBlogs_orders_by_name() 
     { 
      var data = new List<Blog> 
      { 
       new Blog { Name = "BBB" }, 
       new Blog { Name = "ZZZ" }, 
       new Blog { Name = "AAA" }, 
      }.AsQueryable(); 

      var mockSet = new Mock<DbSet<Blog>>(); 
      mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider); 
      mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression); 
      mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType); 
      mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); 

      var mockContext = new Mock<BloggingContext>(); 
      mockContext.Setup(c => c.Blogs).Returns(mockSet.Object); 

      var service = new BlogService(mockContext.Object); 
      service.AddBlog("YYY", "http://blogs.msdn.com/yyyy"); 
      var blogs = service.GetAllBlogs(); 

      Assert.AreEqual(4, blogs.Count); // Fails because whilst the AddBlog is called, and we can verify this, the AsQueryable List doesn't retain the new data 
      Assert.AreEqual("AAA", blogs[0].Name); 
      Assert.AreEqual("BBB", blogs[1].Name); 
      Assert.AreEqual("YYY", blogs[2].Name); 
      Assert.AreEqual("ZZZ", blogs[3].Name); 
     } 
    } 
} 

是否有可能保留了所添加的數據,因此它可以在後面的測試被查詢?

+0

你可以嘗試在你的模擬'CallBase = true'。'var mockSet = new Mock > {CallBase = true};'所以moq會調用DbSet的Add方法。 – nemesv

+0

感謝您的回覆,但這似乎沒有幫助;我是否需要爲添加到底層列表的添加功能設置某種鏈接? –

回答

3

您需要做的是對所有您希望修改集合的方法使用回調函數。動態代理中的回調是調用方法時運行的代碼,並且可以訪問參數。

創建一個ad hoc集合來保存數據(如示例代碼中的data)。然後執行回調(1):對於可以修改集合的動態代理的每種方法。在這些回調中,修改您的特設集合(data)。然後在你的動態代理中,模擬Count方法返回這個集合的計數(data)。

您的專案集合已創建:data

(2)添加一個回調的方法是使用你的服務的一個補充的博客,BlogService.AddBlog我不使用Moq4所以我不會告訴你的語法,但look here, in the callbacks section和使用此語法添加所需的回調。

與嘲諷的東西有許多方法和屬性以及複雜的行爲像一個DbSet<T>的問題是:

  • ,你需要實現很多的回調在dyanmic代理:有多少方法可以修改收藏,如(1)中所述?
  • 或者您需要了解被測代碼的實現細節,才能實現必要的回調,如(2)糟糕!這很醜陋。你可以實現一些可行的方法,但是測試可能會失敗,因爲你沒有嘲笑所需的方法。

要避免此問題,您有兩種解決方案。

  1. 創建一個接口來包裝EF功能。模擬這個界面。此解決方案限制了模擬方法的數量
  2. 使用內存數據庫作爲您的後端,以便您的測試類似於真實的數據庫,並且您不需要模擬任何內容。您只需要爲每個測試初始化​​內存數據庫數據。 Look here,但不要只看公認的答案(哪個順便說一句不是最好的答案)。
+0

回調是我問到的問題的答案,這對於基本的單元測試是完美的;然而,關於內存數據庫實現的額外信息非常有用。我現在正在考慮使用[Effort](http://effort.codeplex.com/)進行更復雜的集成測試。 –

+0

是的,使用內存數據庫使測試更容易:您不必爲EF上下文編寫模擬(實際上有很多模仿非常難以實現,即用多個Include()模擬查詢或更新實體與相關實體:與內存中,這真的很容易)。此外,測試的運行速度與使用mock的單元測試一樣快,因爲它們不會碰到真正的數據庫或需要在磁盤上讀取/寫入。 (我仍然更喜歡將EF與接口解耦,並嘲笑這些接口,因爲我使用了不同的架構,而且我盲目地依賴於真正的接口實現) – JotaBe