调用 PostAsync 后测试 HttpRequestMessage.Content 的值

Posted

技术标签:

【中文标题】调用 PostAsync 后测试 HttpRequestMessage.Content 的值【英文标题】:Test the value of the HttpRequestMessage.Content after calling PostAsync 【发布时间】:2017-01-19 02:08:58 【问题描述】:

我们已经模拟了HttpMessageHandler,因此我们可以测试一个使用HttpClient 的类。

我们正在测试的一个方法创建一个新的HttpClient,调用PostAsync,并释放HttpClient。我们想像这样测试 HTTP 请求的Content

Assert.Equal("", ActualHttpRequestMessage.Content.ReadAsStringAsync());

问题是我们“无法访问已处置的对象”,因为the HttpClient disposes the Content

问题我们如何检查内容?

这是我们的起订量设置。

MockHttpMessageHandler = new Mock<HttpMessageHandler>();

MockHttpMessageHandler
    .Protected()
    .Setup<Task<HttpResponseMessage>>(
        "SendAsync",
        ItExpr.IsAny<HttpRequestMessage>(),
        ItExpr.IsAny<CancellationToken>())
    .Callback<HttpRequestMessage, CancellationToken>(
        (httpRequestMessage, cancellationToken) =>
        
            ActualHttpRequestMessage = httpRequestMessage;
        )
    .Returns(
        Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
        
            Content = new StringContent(string.Empty)
        ));

这就是我们在被测类中使用它的方式。

new HttpClient(HttpMessageHandler);

【问题讨论】:

我不会添加这个作为答案,因为它是我自己的库,但你看过MockHttp 吗?与 Moq 等动态模拟库相比,它为模拟 HttpClient 请求提供了更好的 DSL。 【参考方案1】:

将你的模拟更改为:

MockHttpMessageHandler
    .Protected()
    .Setup<Task<HttpResponseMessage>>(
        "SendAsync",
        ItExpr.IsAny<HttpRequestMessage>(),
        ItExpr.IsAny<CancellationToken>())
    .Callback<HttpRequestMessage, CancellationToken>(
        (httpRequestMessage, cancellationToken) =>
        
            // +++++++++++++++++++++++++++++++++++++++++
            // Read the Content here before it's disposed
            ActualHttpRequestContent = httpRequestMessage.Content
                .ReadAsStringAsync()
                .GetAwaiter()
                .GetResult();

            ActualHttpRequestMessage = httpRequestMessage;
        )
    .Returns(
        Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
        
            Content = new StringContent(string.Empty)
        ));

然后你可以这样测试:

Assert.Equal("", ActualHttpRequestContent);

请记住,我们只能读取一次Content,因此如果您稍后尝试读取它,它将是空的。它就像一个海森堡物体。

【讨论】:

处理内容的不是我们的测试方法。我们正在测试的方法正在这样做。我们的测试方法是调用HttpClient.PostAsync,它会自动处理HttpResponseMessage.Content 我很抱歉。我以为你指的是响应消息。为什么您的单元测试需要在发布后检查请求内容?事先检查一下。发布它不会更改请求内容。 (或者 - 您的单元测试正在检查请求,但您的评论指的是响应) 我们如何在发布前对其进行检查? 您的断言正在测试 ActualHttpRequestMessage.Content。如果您真的想对请求进行单元测试,那么您就是设置内容的人。你确定不应该是 ActualHttpResponseMessage.Content 吗? 是的。我们想测试我们发送的请求。我们不关心回应。基本上,被测方法接受一堆参数,将它们变成HttpClient请求,然后调用PostAsync。我们想测试帖子内容是否应有。

以上是关于调用 PostAsync 后测试 HttpRequestMessage.Content 的值的主要内容,如果未能解决你的问题,请参考以下文章

异步等待 HttpClient.PostAsync 调用

调用 HTTPClient.PostAsync 时设置 Authorization/Content-Type 标头

调用 PostAsync 方法会使应用程序崩溃

使用 httpClient.postasync 进行 web api 调用 .net core

如何等待PostAsync返回?

使用 PostAsync、HttpClient 和 Json 从 Windows Phone 8.1 调用 REST