since i hit the same problem recently, and opted for a simpler solution, wanted to post it here.. this solution requires no Shims, Mocking, nothing expansive etc.
- Pass a 'useDbFunctions' boolean flag to your method with default value as true.
- When your live code executes, your query will use DbFunctions and everything will work. Due to the default value, callers need not worry about it.
- When your unit tests invoke the method to test, they can pass useDbFunctions: false.
- In your method, you can make use the flag to compose your IQueryable..
if useDbFunctions is true, use the DbFunctions to add the predicate to the queryable.
if useDbFunctions is false, then skip the DbFunctions method call, and do an explicit C# equivalent solution.
This way, your unit tests will check almost 95% of your method in parity with live code. You still have the delta of "DbFunctions" vs. your equivalent code, but be diligent about it and the 95% will look like a lot of gain.
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.
}
Unit tests will invoke the method as:
SomeMethodWithDbFunctions(useDbFunctions: false);
Because unit tests would have setup local DbContext entities, the C# logic/DateTime functions would work.