最近我想从dot.net升级到HttpClient netcoreapp2.2
到 netcoreapp3.1
. 我正在使用 https:/docs.microsoft.comen-usaspnetcoretestintegration-tests?view=aspnetcore-3.1。 来执行我的API实现的测试。
有一个测试是验证取消用户是返回499状态码(https:/httpstatuses.com499。). 这个功能的实现是通过中间件完成的。
在测试中,关键断言是这样的。
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.comschrufygroovyassert-api-cancellation。.
在3.1中是否有其他方法来验证用户取消的http请求的响应?还是我在3.1中对API的设置是错误的?是否中间件被执行了,但又被其他3.1的新特性所取代?
看起来像行为的 HttpClient
从2.2改成了3.1
我能够通过使用自定义的 HttpMessageInvoker
而不是 HttpClient
.
制作一些函数来创建一个 HttpMessageInvoker
正在使用 HttpMessageHandler
的 TestServer
(使用WithWebHostBuilder的WebApplicationFactory。):
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));