如何解决.NET Core 3.1中具有AutoMapper,WebApplicationFactory,Entity Framework和DTO的集成测试
我们有一个API,其中包含大约十二种集成测试。所有测试都通过了,直到我添加了一些DTO并使用了AutoMapper。现在,所有测试使用AutoMapper和DTO的方法的测试都失败了。我提供了理解其中一项失败测试所需的所有代码。另外,我阅读了很多有关AutoMapper和以下StackOverflow帖子的信息:
- Integration Testing with AutoMapper fails to initialise configuration
- A kind of integration testing in ASP.NET Core,with EF and AutoMapper
Startup.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错误的原因。然而,使它正常工作是一回事,而使其成为一个好的解决方案则是另一回事。
我强烈建议您使用AutoMapper ProjectTo()
扩展名,您可以直接在IQueryable<T>
序列上使用它。它使您可以一次性将映射和查询结合在一起。它或多或少地根据您的映射执行Select()
,因此它不仅可以为您提供正确的查询结果模型,而且还减少了从数据库中获取的列数,这可以使查询运行更快。但是,当然有其局限性,例如您不能使用自定义类型转换器或条件映射。您可以在documentation中阅读有关Project()
的更多信息。
用法:
public async Task<ActionResult<List<OurDto>>> GetAll()
{
return await _context
.OurModel
.ProjectTo<OutDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}
,
感谢@Prolog的评论。我意识到我需要分别映射IQueryable的每个元素,因此我重写了Controller方法。
此外,旁注:IList.AsQueryable()。ToListAsync()不起作用,所以我写道:
IQueryable<OurDto> dtosQueryable = dtos.AsQueryable();
return await Task.FromResult(dtosQueryable.ToList());
旧控制器方法
[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();
}
新的控制器方法
public async Task<ActionResult<IEnumerable<OurDto>>> GetAll()
{
IQueryable<OurModel> models = _context.OurModel;
IList<OurDto> dtos = new List<OurDto>();
foreach (OurModel model in models)
{
OurDto dto = _mapper.Map<OurDto>(model);
dtos.Add(dto);
}
IQueryable<OurDto> dtosQueryable = dtos.AsQueryable();
return await Task.FromResult(dtosQueryable.ToList());
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。