1

最近我想从 dot.net 升级netcoreapp2.2netcoreapp3.1. 我正在使用https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1为我的 API 实现执行测试。

有一项测试可验证取消用户是否返回 499 状态代码 ( https://httpstatuses.com/499 )。此功能的实现是通过中间件完成的。

在测试中,关键断言如下所示:

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var client = this.CreateHttpClient(repositoryMock);
HttpResponseMessage responseMessage = null;
await Task.WhenAll(
    Task.Run(async () =>
        {
            await Task.Delay(500);
            tokenSource.Cancel();
        }),
    Task.Run(async () =>
        {
            responseMessage = await client.GetAsync(new Uri($"http://localhost/api/values/haxi?haxiIds={haxiGuid}"), token);
        }));
Assert.That(
    responseMessage.StatusCode,
    Is.EqualTo((HttpStatusCode)499));

在 2.2 中一切正常。服务器取消,返回 499 并且 HttpClient 接收它。

在 3.1 中看起来服务器取消,返回 499,但 HttpClient 总是抛出异常:

  Message: 
    System.OperationCanceledException : The operation was canceled.
  Stack Trace: 
    HttpClient.HandleFinishSendAsyncError(Exception e, CancellationTokenSource cts)
    HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
    <<Cancellation_ShouldReturn499>b__3>d.MoveNext() line 57
    --- End of stack trace from previous location where exception was thrown ---
    ControllerTests.Cancellation_ShouldReturn499() line 49
    GenericAdapter`1.BlockUntilCompleted()
    NoMessagePumpStrategy.WaitForCompletion(AwaitAdapter awaitable)
    AsyncToSyncAdapter.Await(Func`1 invoke)
    TestMethodCommand.RunTestMethod(TestExecutionContext context)
    TestMethodCommand.Execute(TestExecutionContext context)
    SimpleWorkItem.PerformWork()

我已经设置了一个全新的解决方案来重现该问题:https ://github.com/schrufygroovy/assert-api-cancellation 。

3.1 中是否有其他方法可以验证用户取消的 http 请求的响应?还是我在 3.1 中设置的 API 错误?中间件是否已执行,但随后被其他一些新的 3.1 功能否决了?

4

1 回答 1

0

看起来HttpClient从 2.2 更改为 3.1的行为

我能够通过使用 customHttpMessageInvoker而不是HttpClient.

HttpMessageInvoker使用HttpMessageHandler使用 WebApplicationFactory 和 WithWebHostBuilder)创建一些函数TestServer

private HttpMessageInvoker CreateHttpMessageInvoker(Mock<IHaxiRepository> repositoryMock)
{
    return new HttpMessageInvoker(this.factory.WithWebHostBuilder(
        builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddScoped(typeof(IHaxiRepository), provider => repositoryMock.Object);
            });
        }).Server.CreateHandler(), true);
}

使用那个代替HttpClient

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
using var httpMessageInvoker = this.CreateHttpMessageInvoker(repositoryMock);
HttpResponseMessage responseMessage = null;
await Task.WhenAll(
    Task.Run(async () =>
        {
            await Task.Delay(500);
            tokenSource.Cancel();
        }),
    Task.Run(async () =>
        {
            responseMessage = await httpMessageInvoker.SendAsync(
                new HttpRequestMessage(HttpMethod.Get, new Uri($"http://localhost/api/values/haxi?haxiIds={haxiGuid}")),
                token);
        }));
Assert.That(
    responseMessage.StatusCode,
    Is.EqualTo((HttpStatusCode)499));
于 2020-06-10T05:30:34.043 回答