Mock HttpClient using Moq - c#

I would like to unit test a class that uses 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, "http://some-domain.in");
//Build the request
var response = await _httpClient.SendAsync(request);
return response;
}
}
Now we would like to unit test the ClassA.SendRequest method. We are using Ms Test for unit testing framework and Moq for mocking.
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?

That particular overload method is not virtual so is unable to be overridden by Moq.
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
Which is why it throws NotSupportedException
The virtual method you are looking for is this method
public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
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 can mock out protected methods, such as SendAsync on the HttpMessageHandler that you can provide to HttpClient in its constructor.
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});
var client = new HttpClient(mockHttpMessageHandler.Object);
Copied from https://www.thecodebuzz.com/unit-test-mock-httpclientfactory-moq-net-core/

Propper mocking with HttpClient is hard work as it was written before most people did unit testing in dotnet. Sometimes I setup a stub HTTP server that returns canned responses based on pattern matching the request url, meaning you test real HTTP requests not mocks but to a localhost server. Using WireMock.net makes this really easy and runs fast enough to satisfy most of my unit testing needs.
So instead of http://some-domain.in use a localhost server setup on some port, and then:
var server = FluentMockServer.Start(/*server and port can be setup here*/);
server.Given(
Request.Create()
.WithPath("/").UsingPost()
)
.RespondWith(
Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody("{'attr':'value'}")
);
You can find a more details and guidance on using wiremock in tests here.

I recently had to mock HttpClient, and I used Moq.Contrib.HttpClient. It was what I needed, and simple to use, so I thought I'd throw it out there.
Here is an example of general usage:
// All requests made with HttpClient go through its handler's SendAsync() which we mock
var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();
// A simple example that returns 404 for any request
handler.SetupAnyRequest()
.ReturnsResponse(HttpStatusCode.NotFound);
// Match GET requests to an endpoint that returns json (defaults to 200 OK)
handler.SetupRequest(HttpMethod.Get, "https://example.com/api/stuff")
.ReturnsResponse(JsonConvert.SerializeObject(model), "application/json");
// Setting additional headers on the response using the optional configure action
handler.SetupRequest("https://example.com/api/stuff")
.ReturnsResponse(bytes, configure: response =>
{
response.Content.Headers.LastModified = new DateTime(2018, 3, 9);
})
.Verifiable(); // Naturally we can use Moq methods as well
// Verify methods are provided matching the setup helpers
handler.VerifyAnyRequest(Times.Exactly(3));
For more info, check out author's blog post here.

Related

How to mock ICorrelationContextAccessor for CorrelationId

For the mocking, I am using Moq 4.16.1 package.
I have a class that derives from DelegatingHandler class
public class Handler : DelegatingHandler
{
private readonly ICorrelationContextAccessor _correlationContextAccessor;
public Handler(ICorrelationContextAccessor correlationContextAccessor)
{
_correlationContextAccessor = correlationContextAccessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
{
request.Headers.Add("x-correlation-Id", _correlationContextAccessor.CorrelationContext.CorrelationId);
var response = await base.SendAsync(request, ct);
response.EnsureSuccessStatusCode();
return response;
}
}
now I need to mock the ICorrelationContextAccessor.
I have tried the below code but had no luck
_correlationContextAccessor.SetupProperty(x => x.CorrelationContext.CorrelationId, new Guid().ToString());
Anyone who how to mock CorrelationId in this situation?
From the source code of CorrelationContext, you can't mock CorrelationId property because it's an only read property.
There is another way you can try to use it, Try to use mock CorrelationContext property by SetupProperty method, then create an instance with your expectation Guid
var guid = new Guid().ToString();
_correlationContextAccessor.SetupProperty(x => x.CorrelationContext, new CorrelationContext(guid,"x-correlation-Id"));

Mock HttpClient with many endpoints and large responses

I mock the SendAsync method of HttpClient using the following code:
var mock = new Mock<HttpMessageHandler>();
Parallel.ForEach(endpoints, (e) =>
{
mock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(
x => x.RequestUri.AbsolutePath.Contains(e.Endpoint)),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() => new HttpResponseMessage()
{
Content = e.Response
});
});
var client = new HttpClient(mock.Object);
where endpoints is a list of objects of the following type:
class Parameters
{
public string Endpoint { get; }
private readonly string _response;
public StringContent Response
{
get { return new StringContent(_response); }
}
}
This mocking approach works as expected, with one caveat. The speed of mocking the protected endpoint drops significantly having added a decent number of endpoints. I need to mock the client for ~5000 endpoints each with ~2MB response. (Designing tests for fewer endpoints and smaller data seems great, but, unfortunately, not an option in my use case). The mocking speed drops significantly having added ~2k endpoints. After ~2h of runtime, it was not able to reach ~3k endpoint.
I am wondering if there is an alternative/better approach of mocking HttpClient ideally with linear asymptotic behavior.
Did you try to eliminate Mock overhead by overriding HttpMessageHandler?
Other potential bottleneck is a Mock<> code which match request URL and endpoint's response. With large number of endpoints it does make sense to optimize that (e.g. MockHttpMessageHandler.SendAsync() to use Dictionary or Lookup to find response).
public class MockHttpMessageHandler : HttpMessageHandler
{
private readonly Dictionary<string, HttpContent> _endpoints;
public MockHttpMessageHandler(Dictionary<string, HttpContent> endpoints)
{
_endpoints = endpoints;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (_endpoints.TryGetValue(request.RequestUri.ToString(), out var content))
return Task.FromResult(new HttpResponseMessage() { Content = content });
return Task.FromResult(new HttpResponseMessage() { StatusCode = HttpStatusCode.NotFound });
}
}

ASP.NET Core mock HttpClient with custom HttpClientHandler

I have trouble mocking with Moq.
Normally having a HttpClient I would mock it by injecting the HttpClient in the base class like this:
public class MyClass
{
private readonly HttpClient httpClient;
public MyClass(HttpClient httpClient)
{
this.httpClient = httpClient;
}
}
But now I have different functions in my class MyClass that need a custom HttpClientHandler like this:
HttpClientHandler httpClientHandler = new HttpClientHandler();
...
using var client = new HttpClient(httpClientHandler);
If I'd simply inject a HttpClient in MyClassTest with var service = new MyClass(httpMock.Object);, then the httpClient would be overwritten.
What would be the right way to test my functions and not making a real HTTP-call?
I suppose you are using typed client approach of the IHttpClientFactory. That's why your MyClass ctor receives an HttpClient instance.
If you need to mock that HttpClient then I suggest you to follow Hamid Mosalla's advice.
In short there is a helper class, which makes HttpMessageHandler's SendAsync mockable (without the need to use the Moq.Protected).
public class FakeHandler: HttpMessageHandler
{
public virtual HttpResponseMessage Send(HttpRequestMessage request)
{
throw new NotImplementedException();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(Send(request));
}
}
You can use this helper class like this to mock any HttpClient call:
var httpResponse = new HttpResponseMessage
{
Content = new StringContent(JsonConvert.SerializeObject(responseObject))
};
var mockHandler = new Mock<FakeHandler> { CallBase = true };
mockHandler
.Setup(handler => handler.Send(It.IsAny<HttpRequestMessage>()))
.Returns(httpResponse);
var mockHttpClient = new HttpClient(mockHandler.Object);
var SUT = new MyClass(mockHttpClient);
What would be the right way to test my functions and not making a real HTTP-call?
Maybe not what you are looking for, but I suggest you consider Andrew Lock's wisdom - don't unit-test API/MVC controllers in ASP.NET Core.
For .NET Core (and .NET 5) you should avoid mocking HttpClient if you are testing a controller class.
If a controller class is not your SUT, I would wrap the HttpClient in a facade interface and mock that.

Add an DelegationHandler (outgoing request middleware) to TestServer's HttpClient

Is it possible to add a custom DelegationHandler (outgoing request middleware) to the HttpClient built by the asp.net core TestServer?
I'm trying to combine the customization possibilities of using HttpClientFactory to influence HttpClients with the in-memory testing utility TestServer:
public class ExternalProxySslDowngradeSimulator : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken token)
{
if (request.RequestUri.Scheme == "https")
request.RequestUri = new Uri(
request.RequestUri.OriginalString.Replace("https:", "http:"));
return base.SendAsync(request, token);
}
}
[TestFixture
public class TestClass
{
[Test]
public async Task CallApi()
{
var builder = new WebHostBuilder()
.ConfigureAppConfiguration((ctx, config) => { })
.UseUrl("http://customDomain.com")
.UseStartup<CustomApi.Startup>();
var testServer = new Microsoft.AspNetCore.TestHost.TestServer(builder);
var httpClient = testServer.CreateClient();
var apiResult = await httpClient.GetAsync("https://customDomain");
}
}
I can't find a way to plug my DelegatingHandler into the HttpClient returned from testServer.CreateClient()
What I've tried so far:
Registering a custom HttpClientBuilder (did not work):
// did not work:
var builder = new WebHostBuilder()
.UseStartup<CustomApi.Startup>()
.ConfigureServices(services =>
{
services
.AddHttpClient("")
.AddHttpMessageHandler<ExternalProxySslDowngradeSimulator>();
Looking through the code for TestServer it is not hooking into that pipeline.
Custom HttpClientFactoryOptions (did not work)
Thought I could bind a custom HttpClientFactoryOptions and add a custom action to HttpClientFactoryOptions.HttpMessageHandlerBuilderActions. But the TestServer doesn't really consume this the way a default Http Client Factory does.
Customize the setup of TestServer (did not work)
Looking through the documentation / source didn't see anywhere to hook in. There's an extension method to further control DI, but I didn't see how that could get me a hook into the custom HttpMessangeHandler that TestServer is using.
So what you do is create your own client using the test server's (Server in code example) CreateHandler method and the HttpClientFactory.Create method.
var client = HttpClientFactory.Create(Server.CreateHandler(), new CustomDelegate() );
client.BaseAddress = Server.BaseAddress;
This took me forever to figure out. Hopefully this helps you.
So your example would be
var httpClient = HttpClientFactory.Create(testServer.CreateHander(), new ExternalProxySslDowngradeSimulator());
httpClient.BaseAddress = testServer.BaseAddress;

Unable to Mock HttpClient PostAsync() in unit tests

I am writing test cases using xUnit and Moq.
I am trying to mock PostAsync() of HttpClient, but I get an error.
Below is the code used for mocking:
public TestADLS_Operations()
{
var mockClient = new Mock<HttpClient>();
mockClient.Setup(repo => repo.PostAsync(It.IsAny<string>(), It.IsAny<HttpContent>())).Returns(() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
this._iADLS_Operations = new ADLS_Operations(mockClient.Object);
}
Error:
Unsupported expression: repo => repo.PostAsync(It.IsAny(),
It.IsAny()) Non-overridable members (here:
HttpClient.PostAsync) may not be used in setup / verification
expressions.
Screenshot:
Non-overridable members (here: HttpClient.PostAsync) may not be used in setup / verification expressions.
I also tried to mock the HttpClient the same way you did, and I got the same error message.
Solution:
Instead of mocking the HttpClient, mock the HttpMessageHandler.
Then give the mockHttpMessageHandler.Object to your HttpClient, which you then pass to your product code class. This works because HttpClient uses HttpMessageHandler under the hood:
// Arrange
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK });
var client = new HttpClient(mockHttpMessageHandler.Object);
this._iADLS_Operations = new ADLS_Operations(client);
Note: You will also need a
using Moq.Protected;
at the top of your test file.
Then you can call your method that uses PostAsync from your test, and PostAsync will return an HTTP status OK response:
// Act
var returnedItem = this._iADLS_Operations.MethodThatUsesPostAsync(/*parameter(s) here*/);
Advantage:
Mocking HttpMessageHandler means that you don't need extra classes in your product code or your test code.
Helpful resources:
Unit Testing with the HttpClient
How to mock HttpClient in your .NET / C# unit tests
As other answers explain, you should mock the HttpMessageHandler or the HttpClientFactory, not HttpClient. This is such a common scenario that someone created a helper library for both cases, Moq.Contrib.HttpClient.
Copying from the General Usage example for HttpClient :
// All requests made with HttpClient go through its handler's SendAsync() which we mock
var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();
// A simple example that returns 404 for any request
handler.SetupAnyRequest()
.ReturnsResponse(HttpStatusCode.NotFound);
// Match GET requests to an endpoint that returns json (defaults to 200 OK)
handler.SetupRequest(HttpMethod.Get, "https://example.com/api/stuff")
.ReturnsResponse(JsonConvert.SerializeObject(model), "application/json");
// Setting additional headers on the response using the optional configure action
handler.SetupRequest("https://example.com/api/stuff")
.ReturnsResponse(bytes, configure: response =>
{
response.Content.Headers.LastModified = new DateTime(2018, 3, 9);
})
.Verifiable(); // Naturally we can use Moq methods as well
// Verify methods are provided matching the setup helpers
handler.VerifyAnyRequest(Times.Exactly(3));
For HttpClientFactory :
var handler = new Mock<HttpMessageHandler>();
var factory = handler.CreateClientFactory();
// Named clients can be configured as well (overriding the default)
Mock.Get(factory).Setup(x => x.CreateClient("api"))
.Returns(() =>
{
var client = handler.CreateClient();
client.BaseAddress = ApiBaseUrl;
return client;
});
Visit Blog
There's inbuilt support to apply conditions on HttpMethod and RequestUri properties of HttpRequestMessage. This way we can mock HttpGet, HttpPost and other verbs for various paths using the EndsWith method as described below.
_httpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", true,
*// Specify conditions for httpMethod and path
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get
&& req.RequestUri.AbsolutePath.EndsWith($"{path}"))),*
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("_0Kvpzc")
});
Instead of directly using an HttpClient instance in your code, use an IHttpClientFactory.
In your tests, you can then create your own implementation of IHttpClientFactory that sends back a HttpClient which connects to a TestServer.
Here's an example of what your Fake Factory could look like:
public class InMemoryHttpClientFactory: IHttpClientFactory
{
private readonly TestServer _server;
public InMemoryHttpClientFactory(TestServer server)
{
_server = server;
}
public HttpClient CreateClient(string name)
{
return _server.CreateClient();
}
}
You can then setup a TestServer in your tests and have your custom IHttpClientFactory create clients for that server:
public TestADLS_Operations()
{
//setup TestServer
IWebHostBuilder hostBuilder = new WebHostBuilder()
.Configure(app => app.Run(
async context =>
{
// set your response headers via the context.Response.Headers property
// set your response content like this:
byte[] content = Encoding.Unicode.GetBytes("myResponseContent");
await context.Response.Body.WriteAsync(content);
}));
var testServer = new TestServer(hostBuilder)
var factory = new InMemoryHttpClientFactory(testServer);
_iADLS_Operations = new ADLS_Operations(factory);
[...]
}
The problem you are having indicates tight coupling, and you can resolve it by introducing an intermediate abstraction. You might want to create a class which aggregates the HttpClient and exposes the PostAsync() method via an interface:
// Now you mock this interface instead, which is a pretty simple task.
// I suggest also abstracting away from an HttpResponseMessage
// This would allow you to swap for any other transport in the future. All
// of the response error handling could be done inside the message transport
// class.
public interface IMessageTransport
{
Task SendMessageAsync(string message);
}
// In ADLS_Operations ctor:
public ADLS_Operations(IMessageTransport messageTransport)
{
//...
}
public class HttpMessageTransport : IMessageTransport
{
public HttpMessageTransport()
{
this.httpClient = //get the http client somewhere.
}
public Task SendMessageAsync(string message)
{
return this.httpClient.PostAsync(message);
}
}

Categories