【发布时间】:2020-07-05 07:15:32
【问题描述】:
我正在开展一个项目,我们必须使用 ASP .NET Core 3.x 开发一个 Web API。到目前为止,一切顺利,运行良好。现在,我正在为这个 Web API 编写一些集成测试,但除了 GET 请求之外,我很难让测试正常工作。
我们正在使用 Jason Taylor 的 Clean Architecture。这意味着我们有一个包含所有请求处理程序的核心项目、一个包含所有数据库实体的域项目和一个用于 API 控制器的演示项目。我们使用MediatR 和依赖注入来进行这些项目之间的通信。
现在,我们遇到了reuqest的body数据没有到达控制器的问题。
控制器中的 Update 方法如下所示:
[ApiController]
[Route("api/[controller]/[action]")]
public abstract class BaseController : ControllerBase
{
private IMediator _mediator;
protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService<IMediator>();
}
public class FavoriteController : BaseController
{
[HttpPut("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Update(long id, UpdateFavoriteCommand command)
{
if (command == null || id != command.Id)
{
return BadRequest();
}
// Sends the request to the corresponding IRequestHandler
await Mediator.Send(command);
return NoContent();
}
}
我们使用 xUnit.net 作为测试框架。 对于集成测试,我们使用的是在夹具类中设置的 InMemory SQLite 数据库。
测试如下所示:
public class UpdateFavoritesTestSqlite : IClassFixture<WebApplicationFactoryWithInMemorySqlite<Startup>>
{
private readonly WebApplicationFactoryWithInMemorySqlite<Startup> _factory;
private readonly string _endpoint;
public UpdateFavoritesTestSqlite(WebApplicationFactoryWithInMemorySqlite<Startup> factory)
{
_factory = factory;
_endpoint = "api/Favorite/Update/";
}
[Fact]
public async Task UpdateFavoriteDetail_WithFullUpdate_ShouldUpdateCorrectly()
{
// Arange
var client = _factory.CreateClient(); // WebApplicationFactory.CreateClient()
var command = new UpdateFavoriteCommand
{
Id = 5,
Caption = "caption new",
FavoriteName = "a new name",
Public = true
};
// Convert to JSON
var jsonString = JsonConvert.SerializeObject(command);
var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
var stringUri = client.BaseAddress + _endpoint + command.Id;
var uri = new Uri(stringUri);
// Act
var response = await client.PutAsync(uri, httpContent);
response.EnsureSuccessStatusCode();
httpContent.Dispose();
// Assert
response.StatusCode.ShouldBe(HttpStatusCode.NoContent);
}
}
如果我们运行测试,我们会收到 400 Bad Request 错误。 如果我们在 Debug 模式下运行测试,我们可以看到代码由于模型状态错误而引发了自定义 ValidationException。这是在演示项目的 DependencyInjection 中配置的:
services
.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var failures = context.ModelState.Keys
.Where(k => ModelValidationState.Invalid.Equals(context.ModelState[k].ValidationState))
.ToDictionary(k => k, k => (IEnumerable<string>)context.ModelState[k].Errors.Select(e => e.ErrorMessage).ToList());
throw new ValidationException(failures);
};
})
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<IWebApiDbContext>());
失败对象包含一个错误,它说:
The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
截图如下:Visual Studio in Debugging mode with json error.
在我读过的一篇 stackoverflow 文章中,删除 [ApiController] 类属性可能会导致更详细的错误描述。在再次调试期间,测试并在带有await Mediator.Send(command); 的行的FavoriteController 的Update 方法中设置断点,我可以看到,到达Update 方法的命令对象只包含空值或默认值,除了id,它是 5。
command
Caption null string
FavoriteName null string
Id 5 long
Public false bool
最令人困惑(和令人沮丧)的部分是,使用 swagger 或 postman 的手动测试都成功了。按照我的理解,集成测试的时候肯定有问题。
我希望有人可以帮助我,看看我缺少什么。会不会是Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory的HttpClient有问题?
【问题讨论】:
标签: c# asp.net-core integration-testing asp.net-core-webapi