2014-01-16 55 views
1

我有一個存儲庫,公開IQueryable和一個服務處理特定的查詢,這裏有幾個使用DbFunctions的方法。爲了可以測試,我使用靜態元素列表創建一個虛假的存儲庫並將其注入到服務中。問題是,由於我的服務查詢列表,並沒有使用數據庫,我得到錯誤「此功能只能從LINQ到實體調用。」。單元測試SqlFunctions

有沒有比測試DbFunctions和QueryProvider更簡單的測試方法?

在此先感謝

+1

這隻會[許多需要替換的酮](http://stackoverflow.com/questions/13332002/how-to-mock-the-limitations-of-entityframeworks-implementation-of-iqueryable/ 13352779#13352779)。做集成測試。模擬將永遠不可靠或需要太多的代碼(+維護),以致它們自己成爲應用程序。你需要爲你的單元測試框架進行單元測試! –

+0

謝謝格特!嘲笑DbFunctions確實很困難,我已經能夠做出一些我需要的東西,但正如你所說,這只是維護。 –

回答

0

你不能可靠地假冒SQL歸因於LINQ對象在許多情況下比LINQ to SQL的行爲不同的事實。例如,其中(new [] { "asdf"}).Contains("ASDF")在LINQ中將對象返回爲false,則LINQ to SQL中相同類型的查詢將返回true。我發現的最好的事情是將數據的檢索與數據的操作分開。也許創建一種將IPersonRepository作爲依賴關係的PersonManager。您可以僞造/模擬IPersonRepository並使用它來測試PersonManager在各種情況下執行它應該執行的操作。

+0

嗨,亞歷克斯,謝謝你的回覆。 我剛剛發現有人在這裏有同樣的問題:http://stackoverflow.com/questions/14883360/how-to-unit-test-getnewvalues-which-contains-entityfunctions-adddays-function 你給的例子可以如果您在EntityFunctionsFake類上創建一個具有相同簽名的Contains方法,並在比較時指定IgnoreCase,則可以使用給定的解決方案來解決該問題。它可能不適合所有情況,但至少我現在可以測試我的要求。你怎麼看? 乾杯 –

+0

儘管您不能在LINQ查詢中使用IgnoreCase。 –

0

,因爲我最近打了同樣的問題,而採用一個簡單的解決方案,希望它張貼在這裏..這個解決方案不需要墊片,嘲諷,沒有什麼膨脹等

  1. 傳遞一個「useDbFunctions」布爾標記爲默認值爲true的方法。
  2. 當您的實時代碼執行時,您的查詢將使用DbFunctions,並且一切都將工作。由於默認值,呼叫者不需要擔心。
  3. 當您的單元測試調用要測試的方法時,它們可以傳遞useDbFunctions:false。
  4. 在你的方法中,你可以使用標誌來組成你的IQueryable .. 如果useDbFunctions爲true,則使用DbFunctions將謂詞添加到可查詢中。 如果useDbFunctions爲false,則跳過DbFunctions方法調用,並執行顯式的C#等效解決方案。

這樣,您的單元測試將檢查您的方法的幾乎95%與現場代碼的奇偶校驗。你仍然有「DbFunctions」與你等價的代碼的差異,但要勤於此,95%看起來會有很大的收益。

public SomeMethodWithDbFunctions(bool useDbFunctions = true) 
{ 
    var queryable = db.Employees.Where(e=>e.Id==1); // without the DbFunctions 

    if (useDbFunctions) // use the DbFunctions 
    { 
    queryable = queryable.Where(e=> 
    DbFunctions.AddSeconds(e.LoginTime, 3600) <= DateTime.Now); 
    } 
    else 
    { 
     // do db-functions equivalent here using C# logic 
     // this is what the unit test path will invoke 
     queryable = queryable.Where(e=>e.LoginTime.AddSeconds(3600) < DateTime.Now); 
    }      

    var query = queryable.Select(); // do projections, sorting etc. 
} 

單元測試將調用方法爲:

SomeMethodWithDbFunctions(useDbFunctions: false); 

因爲單元測試將必須設置本地的DbContext實體,C#的邏輯/日期時間的功能是可行的。

0

我試圖執行DateDiff函數,和它的作品對我來說 但我們應該認爲,在這種情況下,我們測試不同的功能 ,我們在LINQ代碼測試不實的行爲

private class MySqlFunctions 
    { 
     [DbFunction("SqlServer", "DATEDIFF")]//EF will use this function 
     public int? DateDiff(string datePartArg, DateTime startDate, DateTime endDate) 
     { 
      var subtract = startDate.Subtract(endDate); 
      switch (datePartArg) 
      { 
       case "d": 
        return (int?)subtract.TotalDays; 
       case "s": 
        return (int?)subtract.TotalSeconds; // unit test will use this one 
      } 
      throw new NotSupportedException("Method supports only s or d param"); 
     } 
    } 

然後

var sqlFunctions = new TpSqlFunctions(); 

var result = matches.Average(s => sqlFunctions.DateDiff("s", s.MatchCreated, s.WaitingStarted);