2012-04-25 86 views
12

我使用Code First將類映射到現有數據庫。我需要一種方法來對這些映射進行單元測試,這些映射是基於約定,基於屬性和流利api的混合。如何測試實體框架代碼優先映射?

要進行單元測試,我需要確認類的屬性映射到數據庫中正確的表名和列名。該測試需要針對上下文執行,並且應該首先覆蓋代碼的所有配置選項。

在一個很高的水平,我會尋找斷言像(僞代碼):

Assert.IsTrue(context.TableFor<Widget>().IsNamed("tbl_Widget")); 
Assert.IsTrue(context.ColumnFor<Widget>(w => w.Property).IsNamed("WidgetProperty")); 
+1

好吧,我不喜歡EF,如果可能的話評估使用NHibernate的可能性,用NHibernate你可以測試你的映射真的很容易 – Jupaol 2012-04-25 23:56:41

+0

@Jupaol有用的知識,我們同時使用EF和NH,並有我們的問題 – STW 2012-04-25 23:57:31

回答

0

我能想到的涵蓋所有可能的選擇是使用實體的唯一方法Framework Power Tools可預編譯DbContext的視圖,並可能在生成的代碼本身上使用該生成類型的反射和RegEx的組合來驗證所有映射的方式。聽起來對我來說很痛苦。

想到的另一件事是創建一個圍繞DbModelBuilder的外觀來攔截和檢查通過它的所有東西,但我不知道這是否會處理基於約定的東西。也聽起來很痛苦。

作爲一種不太完整但更簡單的替代方案,您可以通過切換到基於屬性的映射來儘可能減少大部分。這將允許您創建一個基礎測試類,比如說,ModelTesting <TEntity>,其中包括使用反射來驗證TEntity有幾個測試方法:

  • 單TableAttribute。
  • 每個屬性都有一個ColumnAttribute或NotMappedAttribute。
  • 至少有一個屬性具有KeyAttribute。
  • 每個屬性類型映射到兼容的數據庫類型。

甚至可以根據屬性和類的名稱(對於每個層次結構類型的警告)強制執行命名約定。也可以檢查外鍵映射。這是一種一次寫入的基類,您可以從每種類型的模型中獲得一次,並捕獲大部分錯誤(無論如何,它捕獲了我的大部分)。

任何無法用屬性表示的東西,比如TPH繼承等等,都變得有點困難。假設你的DbContext沒有爲你生成你的數據庫,那麼集成測試會觸發DbContext並在集合<上執行FirstOrDefault(TEntity >())可能會覆蓋大部分基礎。

+0

感謝您輸入肖恩!我正在考慮一些不那麼全面的方法,比如使用單一配置方法,並會看看EF電動工具,但我並沒有意識到它們。 – STW 2012-04-26 12:52:28

0

如果你寫了一個方法

public static string ToMappingString(this Widget obj) 

然後,你可以很容易地通過認證試驗(www.approvaltests測試此。COM或的NuGet)

這裏有一個視頻:http://www.youtube.com/watch?v=vKLUycNLhgc

但是,如果你正在尋找測試「我的對象保存和retrive自己」 那麼這是「基於理論測試」

的一個完美的地方

理論爲基礎的測試 大多數單元測試採取

Given A,B expect C 

理論的形式爲基礎的測試是

Given A,B expect Theory 

這樣做的好處是沒有必要擔心哪些特定形式的& B都因爲你不需要知道C,所以任何隨機數生成器將工作。

例1:測試加減法

通常你會喜歡

Assert.AreEqual(5, Add(2,3)); 
Assert.AreEqual(9, Add(10,-1)); 
Assert.AreEqual(10, Add(5,5)); 
Assert.AreEqual(7, Subtract(10,3)); 

不過的東西,如果你寫了一個理論測試它看起來像

for(int i = 1; i < 100; i++) 
{ 
    int a = random.Next(); 
    int b = random.Next(); 
    Assert.AreEqual(a, Subtract(Add(a,b),b, string.Format("Failed for [a,b] = [{0},{1}], a,b));   
} 

現在你理解基於理論的測試,你試圖測試的理論是

Given Model A 
When A is stored to the database, and retrieved the resulting object is equal to A 
1

要考慮的另一個想法是使用Linq和ToString()。

對於eaxample這樣的:

context.Widget.Select(c => c.Property).ToString() 

會導致這種用於SQL Server提供者:

"SELECT [Var_3].[WidgetProperty] AS [WidgetProperty] FROM [dbo].[Widget]..." 

現在我們可以隱藏這一切在一些推廣方法和分析結果的SQL它看起來幾乎像您的僞代碼:

Assert.IsTrue(context.Widgets.GetSqlColumnNameFor(w => w.Property).IsNamed("WidgetProperty")); 

擴展草案:

public string GetSqlColumnNameFor<TSource>(this DbSet<T> source, Expression<Func<TSource, TResult>> selector) 
{ 
    var sql = source.Select(selector).ToString(); 

    var columnName = sql... // TODO : Some regex parsing 

    return 
     columnName; 
} 

同樣我們可以創建GetSqlTableNameFor()。

更新:我決定去尋找一些致力於SQL解析器,所以這個方案是比較通用的,顯然是有用於.NET這樣一件事:

http://www.dpriver.com/blog/list-of-demos-illustrate-how-to-use-general-sql-parser/generate-internal-query-parse-tree-in-xml-for-further-processing/