Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock LINQ to Entities helpers such as 'SqlFunctions.StringConvert()'

I am using EF 4 and is trying to unit test the follow line using Moq:

var convertError = models
             .Where(x => SqlFunctions.StringConvert((decimal?) (x.convert ?? 0)) == "0")
             .Any();

and it seems like SqlFunctions.StringConvert() will throw if it detects the context is mocked.

It gives an error saying:

This function can only be invoked from LINQ to Entities

Is it possible to tell SqlFunctions.StringConvert to return a mock object so I can get rid of this error?

like image 808
Chi Chan Avatar asked Feb 19 '13 22:02

Chi Chan


3 Answers

What I did was to provide my own implementations of DbFunctions such that LINQ To Objects in a unit test uses a simple .NET implementation and LINQ To EF at runtime uses the DbFunctionAttribute in the same way System.Data.Entity.DbFunctions would. I had thought about mocking DbFunctions but hey, the LINQ to Objects implementations are useful and work fine. Here is an example:

public static class DbFunctions
{
    [DbFunction("Edm", "AddMinutes")]
    public static TimeSpan? AddMinutes(TimeSpan? timeValue, int? addValue)
    {
        return timeValue == null ? (TimeSpan?)null : timeValue.Value.Add(new TimeSpan(0, addValue.Value, 0));
    }
}
like image 123
Robbie Avatar answered Oct 06 '22 00:10

Robbie


No it is not possible because the function's implementation looks like:

[EdmFunction("SqlServer", "STR")]
public static string StringConvert(decimal? number, int? length)
{
    throw EntityUtil.NotSupported(Strings.ELinq_EdmFunctionDirectCall);
}

You cannot use Moq to fake this function. You need more powerful mocking framework which will allow you replacing static function call - probably Microsoft Fakes, TypeMock Isolator or JustMock.

Or you need to think about your testing approach because mocking the context is the wrong idea. You should instead have something like:

var convertError = myQueryProvider.ConvertQuery(x.convert); 

Where queryProvider will be your mockable type hiding your query. Query is database related logic and it should be tested against the real database. Code around your query is your application logic and it should be unit tested - the best solution to test them both correctly is simply to separate them through some interface (query provider in this case but people often go with a full specific repository). This principle comes from separation of concerns - query execution is separate concern so it is placed into its own method which is tested separately.

like image 44
Ladislav Mrnka Avatar answered Oct 05 '22 23:10

Ladislav Mrnka


You are able to mock EdmFunctions, and I have done this using NSubstitute (which also doesn't support mocking static functions). The trick is to wrap your DbContext in an interface. Then, add your static EdmFunction function to a static class and create an extension method to your context in the static class to call the method. For example

public static class EdmxExtensions
{
   [EdmFunction("SqlServer", "STR")]
   public static string StringConvert(decimal? number, int? length)
   {
      throw EntityUtil.NotSupported(Strings.ELinq_EdmFunctionDirectCall);
   }

   public static IQueryable<Person> MyFunction(this IDbContext context, decimal? number, int? length)
   {
      context.Person.Where(s => StringConvert(s.personId, number, length);
   }

You will then be able to mock MyFunction since it is a method available to an interface, and EntityFramework doesn't get angry when you try to call it.

I have not tried this with Moq, but you may be able to do this in a similar way.

like image 20
lehn0058 Avatar answered Oct 05 '22 22:10

lehn0058