我有一些通过依赖注入接收DbContext的类需要测试。 我正在使用AutoMapper的ProjectTo,因为我的实体通常比我从我的类返回的对象(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>
ProjectTo
的测试与模拟无关。你需要一个真正的提供者。一个真正的内存数据库虽然快,但仍不是真正的东西。你需要更好地理解ProjectTo
的作用以及如何进行测试。 - Lucian Bargaoanu