Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 728 Vote(s) - 3.47 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to Unit Test GetNewValues() which contains EntityFunctions.AddDays function

#1
> Below sample code is working fine in production, but cannot be unit
> tested because the EntityFunctions.

> my unit test project is using
> InMemoryDatabase instead of real SQL database. I can easily solve my
> problem by creating a View in SQL database with computed column
> myValue and newValue. I like to find a way to do the unit test work
> without changing my method and without creating new SQL view

------------------------------------------------------------------------




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][1], I used that approach to solve one of my unit test difficulty, but don't know how to solve this problem.


[1]:

[To see links please register here]

Reply

#2
Rather than call

System.Data.Objects.EntityFunctions.AddDays

directly, I would inject a custom interface, which forwards the call to that method but which can then be mocked for testing purposes.
Reply

#3
I do like to implement ExpressionVisitor as Jean Hominal recommended. My difficulty is how to define the linq2ObjectsSource, visitedSource and visitedQuery in my case. So finally, I just create an Interface for a method IQuerable<myType> GetSelectQuery(IQuerable<EcaseReferralCase> query), then have corresponding class in Production and Test project which is derived from that interface and have implementation of GetSelectQuery(IQuerable<EcaseReferralCase> query). It works fine.


public interface IEntityFunctionsExpressions
{
IQuerable<myType> GetSelectQuery(IQuerable<EcaseReferralCase> query);
}

in production project:


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)

});
}
}

in unit test project:


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

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

then rewrite GetNewValues() method:



public myType GetNewValues()
{
return myrepository.EntityFunctionsExpressions.GetSelectQuery(context.EcaseReferralCases);

}



Reply

#4
Unless I am mistaken, you are going to switch the implementation of the EcaseReferralCases with another `IQueryable`, probably a LINQ To Objects queryable source.

The most robust way would probably be to use an expression visitor to replace calls to `EntityFunctions` with your own, L2Objects compatible functions.

Here is my implementation:

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

And here is a usage sample:

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]);

In that way, you get all the desirable characteristics:

* Developers can continue to use the standard `EntityFunctions` defined by Entity Framework;
* Production implementations are still guaranteed to raise exceptions if not running on the database;
* The queries can be tested against a fake repository;
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through