entity-framework - 在 .NET Core 3.1 中使用 AutoMapper、WebApplicationFactory、Entity Framework 和 DTO 进行集成测试
问题描述
我们有一个包含十几个集成测试的 API。在我添加了一些 DTO 并使用 AutoMapper 之前,所有测试都通过了。现在,所有测试使用 AutoMapper 和 DTO 的方法的测试都失败了。我已经提供了理解其中一个失败测试所需的所有代码。此外,我阅读了很多关于 AutoMapper 和以下 StackOverflow 帖子的内容:
启动.cs
这是我们的 Startup.ConfigureServices()。我已经尝试了每个注释掉和/或标记为“尝试”的代码块。
public void ConfigureServices(IServiceCollection services)
{
services
.AddDbContext<OurContext>(options =>
options.UseSqlServer(Configuration["ConnectionString"]))
.AddDbContext<OurContext>()
.AddRazorPages()
.AddMvcOptions(options => options.EnableEndpointRouting = false)
.AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
services
.AddControllersWithViews();
//ATTEMPTED
//services
// .AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
//ATTEMPTED
//MapperConfiguration mapperConfiguration = new MapperConfiguration(mc =>
//{
// mc.AddProfile(new OurProfile());
//});
//IMapper mapper = mapperConfiguration.CreateMapper();
//services
// .AddSingleton(mapper);
//ATTEMPTED
//services
// .AddAutoMapper(typeof(Startup));
//ATTEMPTED
//var assembly = typeof(Program).GetTypeInfo().Assembly;
//services
// .AddAutoMapper(assembly);
//ATTEMPTED
var assembly = typeof(Program).GetTypeInfo().Assembly;
services.AddAutoMapper(cfg =>
{
cfg.AllowNullDestinationValues = true;
cfg.CreateMap<OurModel, OurDto>()
.IgnoreAllPropertiesWithAnInaccessibleSetter();
}, assembly);
}
控制器
这是我们的控制器。
[Route("api/[controller]")]
[ApiController]
public class OurController : ControllerBase
{
private readonly OurContext _context;
protected readonly ILogger<OurController> Logger;
private readonly IMapper _mapper;
public OurController(OurContext context, ILogger<OurController> logger,
IMapper mapper)
{
_context = context ??
throw new ArgumentNullException(nameof(context));
Logger = logger ??
throw new ArgumentNullException(nameof(logger));
_mapper = mapper ??
throw new ArgumentNullException(nameof(mapper));
}
[HttpGet]
public async Task<ActionResult<IEnumerable<OurDto>>> GetAll()
{
IQueryable<OurModel> models = _context.OurModel;
IQueryable<OurDto> dtos =
_mapper.Map<IQueryable<OurDto>>(models);
return await dtos.ToListAsync();
}
}
配置文件、模型和 DTO
轮廓
public class OurProfile : Profile
{
public OurProfile()
{
CreateMap<OurModel, OurDto>();
}
}
模型
public partial class OurModel
{
public string Number { get; set; }
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Status { get; set; }
public DateTime? Date { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public string District { get; set; }
}
DTO
public class OurDto
{
public string Number { get; set; }
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Status { get; set; }
public DateTime? Date { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public string District { get; set; }
}
测试夹具
这是我们的测试夹具。
public abstract class ApiClientFixture : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
protected abstract string RelativeUrl { get; }
protected ApiClientFixture(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
protected HttpClient CreateClient()
{
HttpClient client;
var builder = new UriBuilder();
client = _factory.CreateClient();
builder.Host = client.BaseAddress.Host;
builder.Path = $"{RelativeUrl}";
client.BaseAddress = builder.Uri;
return client;
}
}
测试
这是我们的测试课。此测试类中的单个测试失败。
public class Tests : ApiClientFixture
{
public Tests(WebApplicationFactory<Startup> factory) : base(factory)
{
}
protected override string RelativeUrl => "api/OurController/";
[Fact]
public async void GetAllReturnsSomething()
{
var response = await CreateClient().GetAsync("");
Assert.True(response.IsSuccessStatusCode);
}
}
当我调试测试时,我看到从提供给内存 API 的 URL 返回了 500 状态代码。
有人有什么建议吗?目前我们一半以上的测试都失败了,我怀疑 AutoMapper 没有为集成测试正确配置。
解决方案
创建地图IQueryable<T>
并不是一个好的解决方案。在您的回答中,您正在失去适当的异步数据库查询流程。我在评论中写道IQueryable<T>
,因为您正在寻找 500 错误原因。让它发挥作用是一回事,让它成为一个好的解决方案是另一回事。
我强烈建议使用 AutoMapperProjectTo()
扩展,您可以直接在IQueryable<T>
序列上使用它。它让您一次性将映射和查询结合起来。它或多或少会Select()
根据您的映射进行操作,因此它不仅可以立即为您提供正确的查询结果模型,而且还可以减少从数据库中获取的列数,从而使查询运行得更快。但是,它当然有一些限制,例如您不能使用自定义类型转换器或条件映射。Project()
您可以在文档中阅读更多信息。
用法:
public async Task<ActionResult<List<OurDto>>> GetAll()
{
return await _context
.OurModel
.ProjectTo<OutDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}
推荐阅读
- python - Python pandas 分组和消除
- azure - 无法拉取映像 - Azure AKS
- simulation - 如何在 AnyLogic 中模拟停车场发生的取货过程?
- laravel - 如何在注册表中创建日期
- java - RabbitMQ 权限不适用
- python - 如何在不删除python asyncio.Queue()中的项目的情况下获取元素
- javascript - 为什么“console.log”在“return”命令后不起作用?
- javascript - JS DOM 问题:未捕获类型错误:无法读取 null 的属性“addEventListener”变为未捕获引用错误
- django - 在 if 语句中传递 django templatetag 返回值
- flutter - 颤振提取人脸并将裁剪后的人脸发送到服务器