.Net核心单元测试错误-源IQueryable没有实现IAsyncEnumerable <...>

如何解决.Net核心单元测试错误-源IQueryable没有实现IAsyncEnumerable <...>

我有一行代码在单元测试中失败,但是在开发和生产中工作得很好。

var result = await _mapper.ProjectTo<GetApplicationsResponse.Application>(pipelineContext.Query).ToListAsync(cancellationToken);

pipelineContext.QueryIQueryable的类型。

我要进行的测试如下

[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 ,这将不起作用。

关于解决方法,有两种方法。

  1. 懒惰/正确的方法:使用更好的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; }
}

这将返回:

enter image description here

很明显,在没有最小的可重现示例的情况下,我已对此进行了简化,但这不应改变方法。我已经假设该集合基于属性名称(查询)为只读,因此使用AddToReadOnlySource进行了安排。如果它不是只读的,则应使用AddRange。

  1. 模拟映射器。

大多数时候,我都是根据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; }
}

结果:

enter image description here

这使用了AsyncEnumerable中的EntityFrameworkCore.Testing类,您可以直接使用它,也可以根据需要将其用作自己实现的基础。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?