HttpClient 模拟

问题描述 投票:0回答:2

.NET 7,xUnit,最小起订量

我在测试时遇到错误:

// Arrange
var mockHttpClientFactory = new Mock<IHttpClientFactory>();
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
var expectedResponseContent = "response content";

mockHttpMessageHandler.Protected()
    .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
    .ReturnsAsync(new HttpResponseMessage
    {
        StatusCode = HttpStatusCode.OK,
        Content = new StringContent(expectedResponseContent)
    });

var httpClient = new HttpClientAdapter(mockHttpMessageHandler.Object)
{
    BaseAddress = new("https://stackoverflow.com")
};

mockHttpClientFactory.Setup(_ => _.CreateClient("someClient"))
    .Returns(httpClient);

var controller = new SomeController(mockHttpClientFactory.Object);

// Act
var result = await controller.GetPayloadAsync();

// Assert
Assert.Equal(expectedResponseContent, result);

错误:

System.Net.Http.HttpRequestException:将内容复制到流时出错。

System.ObjectDisposeException:无法访问关闭的流。

我正在测试的代码:

await client.GetStringAsync("https://stackoverflow.com")

在测试中,我一开始只是用

HttpClient
来嘲笑
HttpClientFactory
,但这显示了错误。然后我实现了this

但我仍然遇到同样的错误。修改如下:

class HttpClientAdapter : HttpClient, IClient
{
    public HttpClientAdapter(HttpMessageHandler httpMessageHandler): base(httpMessageHandler)
    {
    }

    public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) => base.SendAsync(request, cancellationToken);
}

有什么想法如何在不关闭流的情况下测试这个吗?

c# xunit.net .net-7.0
2个回答
1
投票

我做了什么来解决@jdweng 在评论中解释的问题。 我需要重写 2 个 Http 调用的 SendAsync 设置。这是代码。

var handlerIndex = 0;

mockHttpMessageHandler.Protected()
    .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
    .ReturnsAsync(() =>
    {
        if (handlerIndex == 0)
        {
            handlerIndex++; // Increment the index for the second call
            return new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(expectedResponseContent1)
            };
        }
        else
        {
            return new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(expectedResponseContent2)
            };
        }
    })
    .Verifiable();

0
投票

.ReturnsAsync()
中使用委托而不是直接值就足够了。

mockHttpMessageHandler.Protected()
    .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(() =>
                {
                    return new HttpResponseMessage
                    {
                        StatusCode = HttpStatusCode.OK,
                        Content = new StringContent(expectedResponseContent)
                    };
                });

我认为这是因为在这种情况下,Moq 每次都会执行委托代码,因此每次都会创建新的

HttpResponseMessage()
对象。在您的情况下,
HttpResponseMessage()
仅创建一次,因此内部流在后续调用中可能包含不正确的状态。

© www.soinside.com 2019 - 2024. All rights reserved.