如何解决.Net核心单元测试错误-源IQueryable没有实现IAsyncEnumerable <...>
我有一行代码在单元测试中失败,但是在开发和生产中工作得很好。
var result = await _mapper.ProjectTo<GetApplicationsResponse.Application>(pipelineContext.Query).ToListAsync(cancellationToken);
pipelineContext.Query
是IQueryable
的类型。
我要进行的测试如下
[Fact]
public async Task Handle_Success_Returns_GetApplicationsResponse()
{
//Arrange
var sut = CreateSut();
_pipelinesteps
.Setup(steps => steps.GetEnumerator())
.Returns(() => new List<IPipelinestep<GetApplicationsContext>>
{
Mock.Of<IPipelinestep<GetApplicationsContext>>()
}.GetEnumerator());
_mapper.Setup(x => x.ConfigurationProvider)
.Returns(
() => new MapperConfiguration(
cfg =>
{
cfg.CreateMap<Entities.ApplicationsAggregate.Application,GetApplicationsResponse.Application>();
cfg.CreateMap<Entities.ApplicationsAggregate.SiteLocation,GetApplicationsResponse.SiteLocation>();
cfg.CreateMap<Entities.ApplicationsAggregate.SiteAddress,GetApplicationsResponse.SiteAddress>();
}));
//Act
var result = await sut.Handle(new GetApplicationsRequest(),default);
//Assert
result.Should().BeOfType<GetApplicationsResponse>();
_pipelinesteps.Verify(steps => steps.GetEnumerator(),Times.Once);
}
我的局限性在于我无法从_projectTo<...>
进行更改,因为这是新的方法\工作标准。
因此,我很高兴能通过此错误
system.invalidOperationException:源IQueryable没有实现IAsyncEnumerable
。只有实现IAsyncEnumerable的源才能用于Entity Framework异步操作。
----编辑---
之前忘记提及测试正在使用内存数据库
解决方法
问题将是ToListAsync想要一个实现IAsyncEnumerable的序列,但是ProjectTo没有给它一个序列。
您正在使用EntityFrameworkCore内存提供程序,并且我假设您将其注入SUT中,并且在故障点被引用。这是主要问题,因为内存提供程序不提供实现IAsyncEnumerable的序列。 ProjectTo最终向ToListAsync提供了IQueryable
关于解决方法,有两种方法。
- 懒惰/正确的方法:使用更好的DbContext。
以下LINQPad示例使用EntityFrameworkCore.Testing.Moq创建一个可产生IAsyncEnumerable序列的可注入DbContext:
void Main()
{
var fixture = new Fixture();
var dataEntites = fixture.CreateMany<DataEntity>();
var expectedResult = dataEntites.Select(x => new BusinessEntity() { id = x.Id,code = x.Code });
var mapper = new Mapper(new MapperConfiguration(x => x.AddProfile(new MappingProfile())));
var pipelineContext = Create.MockedDbContextFor<PipelineContext>();
pipelineContext.Entities.AddRangeToReadOnlySource(dataEntites);
var sut = new SUT(mapper,pipelineContext);
var actualResult = sut.Handle().Result;
var compareLogic = new CompareLogic();
compareLogic.Config.IgnoreObjectTypes = true;
compareLogic.Config.IgnoreCollectionOrder = true;
var comparisonResult = compareLogic.Compare(expectedResult,actualResult);
Console.WriteLine($"Are the sequences equivalent: {comparisonResult.AreEqual}");
Console.WriteLine(expectedResult);
Console.WriteLine(actualResult);
}
public class SUT
{
IMapper _mapper;
PipelineContext _pipelineContext;
public SUT(IMapper mapper,PipelineContext pipelineContext)
{
_pipelineContext = pipelineContext;
_mapper = mapper;
}
public async Task<List<BusinessEntity>> Handle()
{
return await _mapper.ProjectTo<BusinessEntity>(_pipelineContext.Entities).ToListAsync();
}
}
public class PipelineContext : DbContext
{
public PipelineContext(DbContextOptions<PipelineContext> options) : base(options) { }
public virtual DbSet<DataEntity> Entities { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DataEntity>().HasNoKey().ToView(nameof(DataEntity));
}
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<DataEntity,BusinessEntity>()
.ForMember(d => d.id,o => o.MapFrom(s => s.Id))
.ForMember(d => d.code,o => o.MapFrom(s => s.Code))
.ReverseMap();
}
}
public class DataEntity
{
public Guid Id { get; set; }
public string Code { get; set; }
}
public class BusinessEntity
{
public Guid id { get; set; }
public string code { get; set; }
}
这将返回:
很明显,在没有最小的可重现示例的情况下,我已对此进行了简化,但这不应改变方法。我已经假设该集合基于属性名称(查询)为只读,因此使用AddToReadOnlySource进行了安排。如果它不是只读的,则应使用AddRange。
- 模拟映射器。
大多数时候,我都是根据JBogard对这个主题的评论使用一个真正的Mapper。但是,您似乎可以进行模拟,您可以简单地模拟ProjectTo调用以返回所需的IAsyncEnumerable序列:
void Main()
{
var fixture = new Fixture();
var dataEntites = new AsyncEnumerable<DataEntity>(fixture.CreateMany<DataEntity>());
var expectedResult = new AsyncEnumerable<BusinessEntity>(dataEntites.Select(x => new BusinessEntity() { id = x.Id,code = x.Code }));
var mapperMock = new Mock<IMapper>();
mapperMock.Setup(x => x.ProjectTo<BusinessEntity>(It.IsAny<IQueryable<DataEntity>>(),It.IsAny<object>())).Returns(expectedResult);
var mapper = mapperMock.Object;
var sut = new SUT(mapper);
var actualResult = sut.Handle(dataEntites).Result;
var compareLogic = new CompareLogic();
compareLogic.Config.IgnoreObjectTypes = true;
compareLogic.Config.IgnoreCollectionOrder = true;
var comparisonResult = compareLogic.Compare(expectedResult,actualResult);
Console.WriteLine($"Are the sequences equivalent: {comparisonResult.AreEqual}");
Console.WriteLine(expectedResult);
Console.WriteLine(actualResult);
}
public class SUT
{
IMapper _mapper;
public SUT(IMapper mapper)
{
_mapper = mapper;
}
public async Task<List<BusinessEntity>> Handle(IQueryable<DataEntity> entities)
{
return await _mapper.ProjectTo<BusinessEntity>(entities).ToListAsync();
}
}
public class DataEntity
{
public Guid Id { get; set; }
public string Code { get; set; }
}
public class BusinessEntity
{
public Guid id { get; set; }
public string code { get; set; }
}
结果:
这使用了AsyncEnumerable中的EntityFrameworkCore.Testing类,您可以直接使用它,也可以根据需要将其用作自己实现的基础。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。