我正在将现有的单元测试从 Moq 迁移到 NSubstitute。
对于大多数情况,这是一个非常平滑的过渡,但是当涉及到模拟HttpMessageHandler(SendAsync)时,有一种非常巧妙的方法可以进入moq上的私有方法。
(前面的最小起订量示例,用于设置带有参数表达式的返回)
protected void SetupHandler<T>(Expression<Func<HttpRequestMessage, bool>> match, HttpStatusCode httpStatusCode,
T body)
{
SetupHandlerStringResponse(match, httpStatusCode, JsonConvert.SerializeObject(
body), "application/json");
}
protected void SetupHandlerStringResponse(Expression<Func<HttpRequestMessage, bool>> match, HttpStatusCode httpStatusCode,
string body, string contentType)
{
MockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is(match), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = httpStatusCode,
Content = new StringContent(body, Encoding.UTF8, contentType)
});
}
protected void VerifyHandler(Expression<Func<HttpRequestMessage, bool>> match, Times times)
{
MockHttpMessageHandler.Protected().Verify("SendAsync", times, ItExpr.Is(match), ItExpr.IsAny<CancellationToken>());
}
使用SetupHandler和VerifyHandler我可以轻松地在我的代码中调用设置/验证步骤,如下所示...
SetupHandler(t => t.Method == HttpMethod.Get, HttpStatusCode.OK, lookup);
var response = await Class.DoSomething();
Assert.NotNull(response);
VerifyHandler(t => t.Method == HttpMethod.Get && t.RequestUri.PathAndQuery == $"/api/endpointName", Times.Once());
据我所知,NSubstitute 中没有直接案例支持这一点。
我在网上发现了很多关于此类问题的文章,而且似乎总是存在某种参数问题。我已经多次看到这种方法弹出来替换这个“受保护”访问器
这种方式如果没有参数或不需要参数验证似乎可以工作
但就我而言,如果我无法使用 Arg.Is 表达式来验证输入并仍然模拟 HttpMessageHandler 的输出,那么它对我来说没有用。
任何帮助/想法将不胜感激。
我想我为自己的问题找到了可以接受的答案。
找到了Daniel Ward(罐头中的丹)的[THIS][1]文章,其中概述了 3 个潜在的解决方案。
选项 1:创建一个假的 HttpMessageHandler 类 这对我来说并不是真的,我仍然需要能够使用 NSubstitute 来匹配输入的响应。
选项 2:使用扩展方法中的反射设置对受保护的 SendAsync() 方法的调用 这正是我正在寻找的。正确插入后,它似乎拥有我用最小起订量替换当前设置所需的一切
选项 3:来自 RichardSzalay.MockHttp 的 MockHttpMessageHandler 这个选项也有效,而且看起来效果非常好。但避免了这一点,只是为了向解决方案引入一个新的包。
希望将我的示例指向本文对以后的人有所帮助,甚至更乐观的是,NSubstitute 在库中引入了类似的内容,因此它会像在 Moq 中一样出现在 .Protected() 中。 [1]: https://daninacan.com/how-to-mock-httpclient-in-c-using-nsubstitute-3-ways/