我有一个公开存储IQueryable的存储库,一个服务处理特定的查询,这里有一些使用DbFunctions的方法。为了可测试,我创建了一个带有静态元素列表的虚假存储库,并将其注入服务中。问题是,由于我的服务查询List并且没有使用数据库,我得到错误“这个函数只能从LINQ到实体调用。”。
有没有比创建假DbFunctions和QueryProvider更简单的测试方法?
提前致谢
答案 0 :(得分:1)
我试图实现dateDiff函数,它对我有用 但我们应该认为在这种情况下我们测试不同的功能 我们正在测试不是真实的行为
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");
}
}
然后在linq代码中
var sqlFunctions = new MySqlFunctions();
var result = matches.Average(s => sqlFunctions.DateDiff("s", s.MatchCreated, s.WaitingStarted);
答案 1 :(得分:0)
由于LINQ to对象在许多情况下的行为与LINQ to SQL不同,因此无法可靠地伪造SQL。例如,(new [] { "asdf"}).Contains("ASDF")
在LINQ对象中返回false时,LINQ to SQL中的相同类型的查询将返回true。我发现做的最好的事情是将数据检索与该数据的操作分开。也许创建某种PersonManager,它将IPersonRepository作为依赖项。您可以伪造/模拟IPersonRepository并使用它来测试PersonManager在各种情况下执行它应该执行的操作。
答案 2 :(得分:0)
因为我最近遇到了同样的问题,并选择了一个更简单的解决方案,想在这里发布..这个解决方案不需要Shims,Mocking,没有任何扩展等等。
这样,您的单元测试将检查几乎95%的方法与实时代码相同。你仍然有“DbFunctions”的delta与你的等效代码相比,但要勤奋,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#logic / DateTime函数可以工作。