模拟并验证传递给 PostAsJsonAsync 的请求对象

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

是否有方法验证传递给 HttpClient.PostAsJsonAsync 的请求对象。请求对象在要进行单元测试的方法内部构造。因此我需要验证请求对象是否使用正确的值构造。

我尝试了以下操作,我希望通过比较如下所示的值来验证 searchrequest 对象

public async Task Test()
{
    //Arrange
    var handlerMock = new Mock<HttpClientHandler>();
    handlerMock.Protected()
        .Setup<Task<HttpResponseMessage>>(
            "SendAsync",
            ItExpr.IsAny<HttpRequestMessage>(),
            ItExpr.IsAny<CancellationToken>()
        )
        .ReturnsAsync(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent("{'foo':'bar'}")
        });
    httpClient = new HttpClient(handlerMock.Object);

    //ACT
    var criteria = new Criteria();
    await SomeMethodToTestAsync(criteria);

    //Assert
    var publishedDateStart = new DateOnly(2021, 10, 17);
    handlerMock.Protected().Verify(
        "SendAsync",
        Times.Exactly(1), // we expected a single external request
        ItExpr.Is<HttpRequestMessage>(req =>
            req.Method == HttpMethod.Post  // we expected a POST request
        ),
        ItExpr.IsAny<CancellationToken>(),
        ItExpr.Is<SearchRequest>(r => r.PublishedDateStart == publishedDateStart)   // this doesn't work
  );
}

//Method to be tested
public async Task SomeMethodToTestAsync(Criteria criteria)
{
    var url = "https://example.com";
    // build some complext request from criteria
    var searchRequest = new SearchRequest
    {
        PublishedDateStart = new DateOnly(2021, 10, 17)
    };
    var searchResponse = await httpClient.PostAsJsonAsync(url, searchRequest);

    //other code
}
c# moq
1个回答
0
投票

PostAsJsonAsync
扩展方法不会将
SearchRequest
传递给
SendAsync
。它将
SearchRequest
序列化为 JSON 并将该字符串打包到某种
HttpContent
对象中。这意味着在验证
SendAsync
调用时,测试必须检查
HttpRequestMessage
方法生成的
PostAsJsonAsync

虽然确实不推荐,但您可以通过将

Verify
调用更改为如下所示来实现该目标:

handlerMock.Protected().Verify(
    "SendAsync",
    Times.Exactly(1), // we expected a single external request
    ItExpr.Is<HttpRequestMessage>(req =>
        req.Method == HttpMethod.Post && // we expected a POST request
        req.Content.ReadAsStringAsync().Result.Contains("2021-10-17")
    ),
    ItExpr.IsAny<CancellationToken>());

虽然这通过了测试,但确实不推荐:

  • ItExpr.Is
    谓词需要同步运行,这意味着您需要使用
    Result
    Content
    中提取JSON字符串。不建议这样做,因为它可能会导致死锁。
  • 这样的测试很脆弱,因为它在很大程度上取决于您无法控制的实现细节。

如果在更新依赖项时

PostAsJsonAsync
的实现或
HttpClient
的内部工作方式发生变化,则可能很容易破坏此类测试。您最终会陷入一场令人沮丧的“图书馆打地鼠”游戏。 脆弱测试通常与基于交互的测试相关。

考虑基于状态的测试

。具体来说,您可以考虑实现特定于测试的 Fake HttpClientHandler,而不是使用 Moq。

internal sealed class FakeHttpClientHandler : HttpClientHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        // Implement enough logic here to simulate the service that the
        // client will interact with.
    }
}

另一种选择是建立一个
自托管服务

并对其进行测试。

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