gpt4 book ai didi

.net-core - 使用 AutoMapper ProjectTo 和 Moq.EntityFrameworkCore 进行单元测试

转载 作者:行者123 更新时间:2023-12-05 06:15:37 27 4
gpt4 key购买 nike

我有一些类通过依赖注入(inject)接收 DbContexts,我想测试一下。我正在使用 AutoMapper 的 ProjectTo,因为我的实体通常比我从类(class)返回的对象 (dto) 大得多。我真的很喜欢让 AutoMapper 调整我的查询,以便它只选择我的 DTO 中的字段。

我一直在尝试使用 Moq.EntityFrameworkCore 模拟我的 DbContext。它工作得相对较好,但它确实会导致 AutoMapper ProjectTo() 出现问题。我最终得到了 InvalidCastException。

显然,我对“测试 AutoMapper”或我的 DbContext 不感兴趣,我只想测试我的代码。但是,我无法测试我的代码,因为它在投影上崩溃了。

这是一个极简主义的重现,使用 AutoFixture 稍微缩短了代码,我把所有东西都放在一个文件中,这样任何人都可以轻松地自己尝试:

using AutoFixture;
using AutoFixture.AutoMoq;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Moq;
using Moq.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;

namespace UnitTestEFMoqProjectTo
{
public class MyBusinessFixture
{
private IFixture _fixture;
public MyBusinessFixture()
{
_fixture = new Fixture()
.Customize(new AutoMoqCustomization());

var mockMapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new MappingProfile());
});
var mapper = mockMapper.CreateMapper();
_fixture.Register(() => mapper);
}

[Fact]
public async Task DoSomething_WithMocksAndProjectTo_ValidatesMyLogic()
{
// Arrange
var mockContext = new Mock<MyDbContext>();
mockContext.Setup(x => x.MyEntities).ReturnsDbSet(new List<MyEntity>(_fixture.CreateMany<MyEntity>(10)));
_fixture.Register(() => mockContext.Object);
var business = _fixture.Create<MyBusiness>();

// Act
await business.DoSomething();

// Assert
Assert.True(true);
}
}

public class MyDbContext : DbContext
{
public virtual DbSet<MyEntity> MyEntities { get; set; }
}
public class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string SomeProperty { get; set; }
public string SomeOtherProperty { get; set; }
}

public class MyDto
{
public int Id { get; set; }
public string Name { get; set; }
}
public interface IMyBusiness
{
Task DoSomething();
}
public class MyBusiness : IMyBusiness
{
private readonly MyDbContext _myDbContext;
private readonly IMapper _mapper;
public MyBusiness(MyDbContext myDbContext, IMapper mapper)
{
_myDbContext = myDbContext;
_mapper = mapper;
}
public async Task DoSomething()
{
// My program's logic here, that I want to test.

// Query projections and enumeration
var projectedEntities = await _mapper.ProjectTo<MyDto>(_myDbContext.MyEntities).ToListAsync();

// Some more of my program's logic here, that I want to test.
}
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<MyEntity, MyDto>();
}
}
}

应该输出以下错误:

Message: 
System.InvalidCastException : Unable to cast object of type 'Moq.EntityFrameworkCore.DbAsyncQueryProvider.InMemoryAsyncEnumerable`1[UnitTestEFMoqProjectTo.MyEntity]' to type 'System.Linq.IQueryable`1[UnitTestEFMoqProjectTo.MyDto]'.
Stack Trace:
ProjectionExpression.ToCore[TResult](Object parameters, IEnumerable`1 memberPathsToExpand)
ProjectionExpression.To[TResult](Object parameters, Expression`1[] membersToExpand)
Extensions.ProjectTo[TDestination](IQueryable source, IConfigurationProvider configuration, Object parameters, Expression`1[] membersToExpand)
Mapper.ProjectTo[TDestination](IQueryable source, Object parameters, Expression`1[] membersToExpand)
MyBusiness.DoSomething() line 79
MyBusinessFixture.DoSomething_WithMocksAndProjectTo_ShouldMap() line 39

知道如何继续使用 AutoMapper 进行预测,同时让单元测试正常工作吗?

供引用,这是我的项目文件内容:

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" />
<PackageReference Include="AutoMapper" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="Moq" Version="4.14.1" />
<PackageReference Include="Moq.EntityFrameworkCore" Version="3.1.2.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

</Project>

最佳答案

前几天我遇到了类似的问题,但最终我能够运行我的单元测试。

我使用 MockQueryable.Moq ( https://github.com/romantitov/MockQueryable ) 来模拟 DBSet,您可能应该试试这个包,因为这里可能有问题。

所以我的代码看起来像这样:

public class Project
{
public int Id { get; set; }
public string Name { get; set; }
}

public class ProjectDto
{
public int Id { get; set; }
public string Name { get; set; }
}

class GetProjectsHandler : IRequestHandler<GetProjectsRequest, GetProjectsResponse>
{
private readonly IMyDbContext context;
private readonly IMapper mapper;

public GetProjectsHandler(IMyDbContext context,IMapper mapper)
{
this.context = context;
this.mapper = mapper;
}

public async Task<GetProjectsResponse> Handle(GetProjectsRequest request, CancellationToken cancellationToken)
{
IReadOnlyList<ProjectDto> projects = await context
.Projects
.ProjectTo<ProjectDto>(mapper.ConfigurationProvider)
.ToListAsync(cancellationToken);

return new GetProjectsResponse
{
Projects = projects
};
}
}

简化的单元测试如下所示:

    public class GetProjectsTests
{
[Fact]
public async Task GetProjectsTest()
{
var projects = new List<Project>
{
new Project
{
Id = 1,
Name = "Test"
}
};
var context = new Mock<IMyDbContext>();
context.Setup(c => c.Projects).Returns(projects.AsQueryable().BuildMockDbSet().Object);

var mapper = new Mock<IMapper>();
mapper.Setup(x => x.ConfigurationProvider)
.Returns(
() => new MapperConfiguration(
cfg => { cfg.CreateMap<Project, ProjectDto>(); }));


var getProjectsRequest = new GetProjectsRequest();
var handler = new GetProjectsHandler(context.Object, mapper.Object);
GetProjectsResponse response = await handler.Handle(getProjectsRequest, CancellationToken.None);


Assert.True(response.Projects.Count == 1);
}
}

我同意我们应该如何在单元测试中模拟 DBSet 是一个值得商榷的问题。我认为 InMemoryDatabase 应该用于集成测试而不是单元测试。

关于.net-core - 使用 AutoMapper ProjectTo 和 Moq.EntityFrameworkCore 进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62411066/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com