我正在使用 Moq 框架用 C# 编写单元测试。我如何为以下内容编写模拟语句:
var HttpClient httpClient;
var request = new HttpRequestMessage() { ... }
httpClient.Send(request);
我已经找到了模拟 SendAsync 方法的解决方案,但不仅仅是 Send() 方法。由于我正在编写单元测试,因此我不希望我的测试向 URL 发送实际请求。
因此,我想模拟它并验证是否调用了 Send 方法。应提供什么 URL 来确保此行为,而不遇到任何其他问题?
我无法修改我的原始代码。是否可以使用 HttpMessageHandler 来执行此操作?我应该提供什么 URL 来模拟此语句,以及如何在不向 URL 发出实际请求且不会遇到其他问题(例如无效主机或无法连接到主机等)的情况下模拟此语句?谢谢你。
此方法允许您覆盖受保护的方法。 Handler 是实际进行调用的对象。如果您检查 HttpClient 发送并跟踪它,您最终会看到对 HttpMessageHandler 的调用。
[TestMethod]
public void Can_Send_HttpClient_Message()
{
var mockedHttpMessageHandler = new Mock<HttpMessageHandler>();
mockedHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage(System.Net.HttpStatusCode.OK));
mockedHttpMessageHandler
.Protected()
.Setup<HttpResponseMessage>("Send", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Returns(new HttpResponseMessage(System.Net.HttpStatusCode.OK));
var httpClient = new HttpClient(mockedHttpMessageHandler.Object, false);
var request = new HttpRequestMessage(HttpMethod.Post, "https://www.test.com");
httpClient.Send(request);
}
为了使代码具有良好的可扩展性和可测试性,同时也为了避免违反 SOLID 原则,我建议引入几个抽象,如下所示。该方法使用装饰器(包装器)模式,主要思想是为
HttpClient
类提供一个接口,该接口很容易注入到您的代码和模拟中。
public interface IHttpClient
{
HttpResponseMessage Send(HttpRequestMessage request);
}
public class HttpClientWrapper : IHttpClient
{
private readonly HttpClient _httpClient;
public HttpClientWrapper()
{
_httpClient = new HttpClient();
}
public HttpResponseMessage Send(HttpRequestMessage request)
{
return _httpClient.Send(request);
}
}
之后,在您的代码中使用类似的东西,注入
IHttpClient
:
public class MyService : IMyService
{
private readonly IHttpClient _httpClient;
public MyService(IHttpClient httpClient)
{
_httpClient = httpClient;
}
public void SendSomeRequest()
{
_httpClient.Send(new HttpRequestMessage());
// other code goes here...
}
}
最后,您可以像往常一样使用
IHttpClient
框架模拟这个 Moq
依赖项。