是否有方法验证传递给 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
}
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.
}
}
另一种选择是建立一个自托管服务