C# 单元测试函数
我有一个公开IQueryable的存储库和一个处理特定查询的服务,这里有一些使用DbFunctions的方法。为了便于测试,我创建了一个包含静态元素列表的假存储库,并将其注入到服务中。问题是,由于我的服务查询一个列表而不使用数据库,所以我得到一个错误“这个函数只能从LINQ调用到实体” 有没有比创建假DbFunctions和QueryProvider更简单的方法来测试它C# 单元测试函数,c#,entity-framework,unit-testing,mocking,C#,Entity Framework,Unit Testing,Mocking,我有一个公开IQueryable的存储库和一个处理特定查询的服务,这里有一些使用DbFunctions的方法。为了便于测试,我创建了一个包含静态元素列表的假存储库,并将其注入到服务中。问题是,由于我的服务查询一个列表而不使用数据库,所以我得到一个错误“这个函数只能从LINQ调用到实体” 有没有比创建假DbFunctions和QueryProvider更简单的方法来测试它 提前感谢您无法可靠地伪造SQL,因为在许多情况下,LINQ to对象的行为与LINQ to SQL不同。例如,如果(new[]
提前感谢您无法可靠地伪造SQL,因为在许多情况下,LINQ to对象的行为与LINQ to SQL不同。例如,如果
(new[]{“asdf”}).Contains(“asdf”)
在LINQ to对象中返回false,那么LINQ to SQL中相同类型的查询将返回true。我发现最好的办法是将数据的检索与对该数据的操作分开。也许可以创建某种PersonManager,将IPersonRepository作为依赖项。您可以伪造/模拟IPersonRepository,并使用它来测试PersonManager在各种情况下是否执行了它应该执行的操作 因为我最近遇到了同样的问题,并且选择了一个更简单的解决方案,所以我想把它发布在这里。。此解决方案不需要填隙片、模拟,也不需要膨胀等
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.
}
因为单元测试将设置本地DbContext实体,所以C#logic/DateTime函数将起作用。我尝试实现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);
嗨,亚历克斯,谢谢你的回复。我刚刚发现有人在这里遇到了同样的问题:如果您在EntityFunctionsFake类上创建了一个具有相同签名的Contains方法,并在比较时指定IgnoreCase,那么您给出的示例可以使用给定的解决方案来解决。它可能不适合所有情况,但至少我现在可以测试我的需求。你怎么认为?谢谢,但是你不能在LINQ查询中使用IgnoreCase。那只能是。做集成测试。mock永远不会可靠,或者需要太多的代码(+维护),以致于它们自己成为一个应用程序。您需要为您的单元测试框架进行单元测试!谢谢你,格特!模拟DbFunctions确实很困难,我已经能够制作出一些我需要的函数,但正如您所说的,这只是维护。
var sqlFunctions = new MySqlFunctions();
var result = matches.Average(s => sqlFunctions.DateDiff("s", s.MatchCreated, s.WaitingStarted);