2013-02-14 51 views
2

下面的示例代碼工作在製作精細,但也不能因爲EntityFunctions測試單元 單元測試GetNewValues()。如何包含EntityFunctions.AddDays功能

我的單元測試項目使用的是InMemoryDatabase而不是真正的SQL數據庫。通過在SQL數據庫中創建一個計算列 myValue和newValue,我可以輕鬆解決我的 問題。我想找到一種方法做單元測試工作 不改變我的方法,並不會產生新的SQL視圖


public class EcaseReferralCaseRepository : Repository 
{ 

     public class myType 
     { 
       public DateTime myValue; 
       public DateTime newValue; 
     } 

     public myType GetNewValues() 
     { 
       return 
         (myType)(from o in context.EcaseReferralCases 
         select new myType 
         { 
          // LINQ to Entity 
          myValue = (DateTime)System.Data.Objects.EntityFunctions.AddDays(o.StartDate, 0), 
          newValue = (DateTime)System.Data.Objects.EntityFunctions.AddDays(o.StartDate, 30) 

          // LINQ to Object 
          //myValue = o.StartDate.AddDays(0), 
          //newValue = o.StartDate.AddDays(30) 

         }); 
     } 
} 

This link shows a good example to unit test EntityFunctions,我用這種方法來解決我單位的一個測試困難,但不知道如何解決這個問題。

+0

你的問題是什麼? – 2013-02-14 20:38:42

+0

標題是我的問題!如何對方法進行單元測試GetNewValues() – user2073400 2013-02-14 20:43:57

+0

從測試中您將獲得什麼? – 2013-02-14 21:09:57

回答

1

比呼叫

System.Data.Objects.EntityFunctions.AddDays 

直接相反,我將注入的自定義接口,該接口將呼叫轉發到該方法,但其然後可以被模擬用於測試目的。

+0

我做了同樣的:從myContext.myEntities中的o where(myExpression)選擇o.startDate。但現在,我的問題:從myContext.myEntities中的o選擇EntityFunctions.AddDays(o.startDate,30)。這次,EntityFunctions.AddDays在選定的列中,而不是在where語句中 – user2073400 2013-02-15 00:52:09

+0

Hi,500 - 內部服務器錯誤;我喜歡你的建議,但我不知道如何在我的查詢中實現select語句的customized部分。你能提供更多的細節信息嗎?謝謝 – user2073400 2013-02-19 19:44:15

+0

@ 500從Linq to Entities表達式調用時不會導致可怕的「無法識別的方法」異常? – guillaume31 2013-04-08 15:35:51

5

除非我誤解了,否則您將切換實施EcaseReferralCases與另一個IQueryable,可能是一個LINQ To Objects可查詢源。

最可靠的方法可能是使用表達式訪問者用您自己的L2Objects兼容函數替換對EntityFunctions的調用。

這是我實現:

using System; 
using System.Data.Objects; 
using System.Linq; 
using System.Linq.Expressions; 

static class EntityFunctionsFake 
{ 
    public static DateTime? AddDays(DateTime? original, int? numberOfDays) 
    { 
     if (!original.HasValue || !numberOfDays.HasValue) 
     { 
      return null; 
     } 
     return original.Value.AddDays(numberOfDays.Value); 
    } 
} 
public class EntityFunctionsFakerVisitor : ExpressionVisitor 
{ 
    protected override Expression VisitMethodCall(MethodCallExpression node) 
    { 
     if (node.Method.DeclaringType == typeof(EntityFunctions)) 
     { 
      var visitedArguments = Visit(node.Arguments).ToArray(); 
      return Expression.Call(typeof(EntityFunctionsFake), node.Method.Name, node.Method.GetGenericArguments(), visitedArguments); 
     } 

     return base.VisitMethodCall(node); 
    } 
} 
class VisitedQueryProvider<TVisitor> : IQueryProvider 
    where TVisitor : ExpressionVisitor, new() 
{ 
    private readonly IQueryProvider _underlyingQueryProvider; 
    public VisitedQueryProvider(IQueryProvider underlyingQueryProvider) 
    { 
     if (underlyingQueryProvider == null) throw new ArgumentNullException(); 
     _underlyingQueryProvider = underlyingQueryProvider; 
    } 

    private static Expression Visit(Expression expression) 
    { 
     return new TVisitor().Visit(expression); 
    } 

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) 
    { 
     return new VisitedQueryable<TElement, TVisitor>(_underlyingQueryProvider.CreateQuery<TElement>(Visit(expression))); 
    } 

    public IQueryable CreateQuery(Expression expression) 
    { 
     var sourceQueryable = _underlyingQueryProvider.CreateQuery(Visit(expression)); 
     var visitedQueryableType = typeof(VisitedQueryable<,>).MakeGenericType(
      sourceQueryable.ElementType, 
      typeof(TVisitor) 
      ); 

     return (IQueryable)Activator.CreateInstance(visitedQueryableType, sourceQueryable); 
    } 

    public TResult Execute<TResult>(Expression expression) 
    { 
     return _underlyingQueryProvider.Execute<TResult>(Visit(expression)); 
    } 

    public object Execute(Expression expression) 
    { 
     return _underlyingQueryProvider.Execute(Visit(expression)); 
    } 
} 
public class VisitedQueryable<T, TExpressionVisitor> : IOrderedQueryable<T> 
    where TExpressionVisitor : ExpressionVisitor, new() 
{ 
    private readonly IQueryable<T> _underlyingQuery; 
    private readonly VisitedQueryProvider<TExpressionVisitor> _queryProviderWrapper; 
    public VisitedQueryable(IQueryable<T> underlyingQuery) 
    { 
     _underlyingQuery = underlyingQuery; 
     _queryProviderWrapper = new VisitedQueryProvider<TExpressionVisitor>(underlyingQuery.Provider); 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return _underlyingQuery.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public Expression Expression 
    { 
     get { return _underlyingQuery.Expression; } 
    } 

    public Type ElementType 
    { 
     get { return _underlyingQuery.ElementType; } 
    } 

    public IQueryProvider Provider 
    { 
     get { return _queryProviderWrapper; } 
    } 
} 

這裏是一個用法示例:

var linq2ObjectsSource = new List<DateTime?>() { null }.AsQueryable(); 
var visitedSource = new VisitedQueryable<DateTime?, EntityFunctionsFakerVisitor>(linq2ObjectsSource); 
var visitedQuery = visitedSource.Select(dt => EntityFunctions.AddDays(dt, 1)); 
var results = visitedQuery.ToList(); 
Assert.AreEqual(1, results.Count); 
Assert.AreEqual(null, results[0]); 

這樣一來,你的所有期望的特性:

  • 開發人員可以繼續使用實體框架定義的標準EntityFunctions;
  • 如果不在數據庫上運行,生產實現仍然保證會引發異常;
  • 查詢可以針對僞造的存儲庫進行測試;
+0

非常感謝。我會在我的真實項目中嘗試。 – user2073400 2013-02-15 13:22:46

+0

請注意,QueryProvider中的'TExpressionVisitor'參數可能是一個過度工程的情況,這取決於您的需求。 – 2013-02-15 14:50:35

+0

我未能在我的項目中實施您的方法。在我的情況下,我很難設置linq2ObjectsSource,visitedSource和visitedQuery。 – user2073400 2013-02-25 14:19:04

0

我確實喜歡像Jean Hominal推薦的那樣實現ExpressionVisitor。我的困難是如何在我的情況下定義linq2ObjectsSource,visitedSource和visitedQuery。所以最後,我只是爲方法IQuerable GetSelectQuery(IQuerable query)創建一個接口,然後在Production和Test項目中有相應的類,該項目從該接口派生並具有GetSelectQuery(IQuerable查詢)的實現。它工作正常。

public interface IEntityFunctionsExpressions 
{ 
    IQuerable<myType> GetSelectQuery(IQuerable<EcaseReferralCase> query); 
} 
在生產項目

public class EntityFunctionsExpressions : IEntityFunctionsExpressions 
{ 
    public EntityFunctionsExpressions() 
    { 
    } 

    public IQuerable<myType> GetSelectQuery(IQuerable<EcaseReferralCase> query) 
    { 
     // Expression for LINQ to Entities, does not work with LINQ to Objects 
     return 
        (myType)(from o in query 
        select new myType 
        { 
         // LINQ to Entity 
         myValue = (DateTime)System.Data.Objects.EntityFunctions.AddDays(o.StartDate, 0), 
         newValue = (DateTime)System.Data.Objects.EntityFunctions.AddDays(o.StartDate, 30) 

        }); 
    } 
} 
在單元測試項目

​​

然後重寫GetNewValues()方法:

公共的myType GetNewValues() { 返回myrepository.EntityFunctionsExpressions.GetSelec TQuery的(context.EcaseReferralCases);

}