如何测试实体框架中随时间推移发生的查询数
本文关键字:时间 查询 何测试 测试 框架 实体 | 更新日期: 2023-09-27 18:35:45
对于一些背景 - 我正在编写一些实体框架功能的抽象,以使使用数据访问层的人员的工作更容易一些。我正在通过代理/包装器执行此操作,并且我想测试EF Include()
的成功使用。(但这些都与此处的特定问题无关(只是想避免人们建议"不要测试 EF 功能"(我将 Include 方法委托给 EF,这是我实际测试的内容)))
理想情况下,我想定义一个块(可能通过using
),并让该块计算该块内发生的查询数量。
使用一些伪代码,这是我想要的行为:
var user = new User(id);
using(var queryCounter = new QueryCounter()){
user.Books.SelectMany(b => b.Pages);
Assert.Equal(2, queryCounter.NumberOfDetectedQueries);
// the above assert would fail, due to not using the `Include` keyword.
// as the two queries should be to select books and page ids and
// then to actually select the pages
}
有没有办法实现上述查询计数之类的方法?
更新:
感谢@Ilya Chumakov提供查询拦截器的见解。我已经能够通过一个额外的类获取上面示例中的语法:
public class QueryCounter : IDisposable
{
public int Count => GlobalCounter.QueryCount;
public QueryCounter()
{
GlobalCounter.QueryCount = 0;
GlobalCounter.Active = true;
}
public void Dispose()
{
GlobalCounter.Active = false;
GlobalCounter.QueryCount = 0; //
}
}
然后只需将活动字段添加到全局计数器
public static class GlobalCounter
{
public static int QueryCount = 0;
public static bool Active = false;
}
并修改每个拦截器方法,如下所示:
#if DEBUG
if (!GlobalCounter.Active) return;
GlobalCounter.QueryCount++;
// or whatever output class/method works for you
Trace.Write("Current Query Count: " + GlobalCounter.QueryCount + " -- ");
Trace.WriteLine(command.CommandText);
#endif
现在,我的测试如下所示:
using (var counter = new QueryCounter())
{
var pages = user.Books.First().Pages;
Assert.Equal(1, counter.Count);
}
可以使用拦截器来实现:
class EfCommandInterceptor : IDbCommandInterceptor
{
public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
GlobalCounter.QueryCount++;
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
GlobalCounter.QueryCount++;
}
public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
GlobalCounter.QueryCount++;
}
//other methods are empty
}
GlobalCounter
类保留全局变量:
static class GlobalCounter
{
public static int QueryCount { get; set; }
}
注册拦截器:
public class EntityConfigiration : DbConfiguration
{
public EntityConfigiration()
{
this.AddInterceptor(new EfCommandInterceptor());
}
}
EntityConfigiration
类将自动注册。您可以改为在配置文件中注册拦截器。
实体框架教程:拦截
基于代码的配置(EF6 及更高版本)
然后使用:
[Test]
public void CalculateQueryCount()
{
GlobalCounter.QueryCount = 0;
using (var context = new YourContext())
{
//queries
}
int actual = GlobalCounter.QueryCount;
}
此EfCommandInterceptor
代码是单线程的。对于多线程测试,应该使用锁定(lock
、Interlocked.Increment
等)代替++
。