我喜欢对使用HttpClient的类进行单元测试.我们将HttpClient对象注入到类构造函数中.
I like to unit test a class that use HttpClient. We injected the HttpClient object in the class constructor.
public class ClassA : IClassA { private readonly HttpClient _httpClient; public ClassA(HttpClient httpClient) { _httpClient = httpClient; } public async Task<HttpResponseMessage> SendRequest(SomeObject someObject) { //Do some stuff var request = new HttpRequestMessage(HttpMethod.Post, "some-domain.in"); //Build the request var response = await _httpClient.SendAsync(request); return response; } }现在,我们想对ClassA.SendRequest方法进行单元测试.我们将Ms Test用于单元测试框架,并将Moq用于模拟.
Now we like to unit test the ClassA.SendRequest method. We are using Ms Test for unit testing framework and Moq for mocking.
当我们尝试模拟HttpClient时,它会抛出NotSupportedException.
When we tried to mock the HttpClient, it throws NotSupportedException.
[TestMethod] public async Task SendRequestAsync_Test() { var mockHttpClient = new Mock<HttpClient>(); mockHttpClient.Setup( m => m.SendAsync(It.IsAny<HttpRequestMessage>())) .Returns(() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))); }我们如何解决这个问题?
How can we solve this issue?
推荐答案该特定的重载方法不是虚拟的,因此无法被Moq覆盖.
That particular overload method is not virtual so is unable to be overridden by Moq.
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);这就是为什么它抛出NotSupportedException
您正在寻找的虚拟方法就是这种方法
The virtual method you are looking for is this method
public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);但是模拟HttpClient并不像使用其内部消息处理程序那样简单.
However mocking HttpClient is not as simple as it seems with its internal message handler.
我建议使用带有自定义消息处理程序存根的具体客户端,以便在伪造请求时提供更大的灵活性.
I suggest using a concrete client with a custom message handler stub that will allow for more flexibility when faking the request.
这里是委派处理程序存根的示例.
Here is an example of a delegating handler stub.
public class DelegatingHandlerStub : DelegatingHandler { private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc; public DelegatingHandlerStub() { _handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK)); } public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc) { _handlerFunc = handlerFunc; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return _handlerFunc(request, cancellationToken); } }请注意,默认构造函数基本上是在做您以前尝试模仿的事情.它还允许带有请求委托的更多自定义方案.
Note the default constructor is doing basically what you were trying to mock before. It also allows for more custom scenarios with a delegate for the request.
使用存根,可以将测试重构为类似的内容
With the stub, the test can be refactored to something like
public async Task _SendRequestAsync_Test() { //Arrange var handlerStub = new DelegatingHandlerStub(); var client = new HttpClient(handlerStub); var sut = new ClassA(client); var obj = new SomeObject() { //Populate }; //Act var response = await sut.SendRequest(obj); //Assert Assert.IsNotNull(response); Assert.IsTrue(response.IsSuccessStatusCode); }更多推荐
使用Moq模拟HttpClient
发布评论