如何测试调用返回 IQueryable 的持久性方法的方法

问题描述 投票:0回答:1

我在一些地方读到,调用数据库返回 IQueryable 的方法是一个很好的实践,因为它允许使用 Select、Where 等方法在服务中扩展查询,并防止将未过滤的数据加载到内存中。

但是,我在测试使用这种方法的方法时遇到了问题。我收到以下错误:

源 IQueryable 没有实现 IAsyncEnumerable。只有实现 IAsyncEnumerable 的源才能用于实体框架异步操作。

我如何在测试中处理此方法,或者我应该以不同的方式处理它?

这是GenerateCustomerReportAsync方法:

public async Task GenerateCustomerReportAsync()
{
    var jurisdictions = await _customerPersistenceService.GetAll()
        .Select(j => new { j.Name, j.SurName }).ToListAsync();
}

以及持久化服务中的方法:

public interface ICustomerPersistenceService
{
    IQueryable<Customers> GetAll();
}

public IQueryable<Customers> GetAll()
{
    return _clientContext.Customers.AsQueryable();
}

这是测试的模拟代码:

var customers = new List<Customer>
{
    new Customer { /* initialize customer properties */ }
}.AsQueryable();

subCustomerPersistenceService = Substitute.For<ICustomerPersistenceService>();
subCustomerPersistenceService.GetAll().Returns(customers);

我尝试在测试中使用 .AsQueryable() 作为列表。我希望它能够模拟从数据库返回的 IQueryable 集合的行为,从而使我的方法能够无错误地执行。我正在考虑使用表达式发送选择器,然后返回列表,但我不确定这是否是最好的方法,或者我是否应该只使用 IEnumerable 和 AsEnumerable()。

c# performance linq entity-framework-core iqueryable
1个回答
0
投票

它一直有效,直到您想要针对

async
执行
IQueryable
操作。 例如,如果您的测试方法看起来像这样:

public async Task<IEnumerable<CustomerViewModel>> GetRecentCustomers(int pageNumber, int pageSize)
{
    var cutoffDate = DateTime.Today.AddDays(-7);
    var customers = await _customerService.GetAll()
        .Where(c => c.Orders.Any(o => o.OrderDate >= cutoffDate)
        .ProjectTo<CustomerViewModel>(_config)
        .Skip(pageNumber * pageSize)
        .Take(pageSize)
        .ToListAsync();
    return customers;
}

...模拟返回常规

IQueryable
List<T>
不起作用,因为底层
List<T>
不支持
async
操作。幸运的是,有一个实现可以将
List<T>
模拟为通过异步调用工作的东西,即 MockQueryable。 (https://www.nuget.org/packages/MockQueryable.Core)那么唯一的变化是:

var customers = new List<Customer>
{
    new Customer { /* initialize customer properties */ }
}.AsQueryable();

...您使用:

var customers = new List<Customer>
{ 
    new Customer { /* initialize customer properties */ }
}.BuildMock(); // Returns your IQueryable<Customer>

我建议对所有模拟的 IQueryable 使用 BuildMock,这样,如果您将代码从同步重构为异步,而不破坏现有测试,您的测试将继续工作。

© www.soinside.com 2019 - 2024. All rights reserved.