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);
}
}
Related
Update 20221024: I have used Ruikai Feng's solution in order to use Mockoon with my tests. I realize this is not a correct approach from a unit testing approach and am working to change my approach.
Update 20221019: I have been using moq to mock out the IHttpClientFactory. The reason why I wanted to instantiate it was to call mock apis created in a tool called Mockoon which replicates apis. I have been so far unable to call these APIs likely because I have not yet properly mocked the ihttpclientfactory. I appreciate all the feedback as the solution is still ongoing at this time.
I am using a .NET 6 Web API controller with IHttpClientFactory to perform external API calls. As such, I have the following constructor:
public MyController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
This works because in my Program.cs I add an HTTP Client to my builder.Services.
In my tests, how do I instantiate/set up the httpClientFactory for the controller because I need it to instantiate my controller: var controller = new MyController(httpClientFactory); generates an error since there isn't any settings added.
I ran into a similar issue with configurations from appsettings.json and resolved with ConfigurationBuilder but there doesn't seem to be a similar one for IHttpClientFactory.
If you need any more information, please let me know. Thanks!
In order to be able to use a properly mocked IHttpClientFactory in your unit test you need to do the following steps:
Setup a DelegatingHandler mock
var mockHandler = new Mock<DelegatingHandler>();
mockHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK))
.Verifiable();
mockHandler.As<IDisposable>().Setup(s => s.Dispose());
This sample mock will always return with 200 OK status code and without a response body
Tailor the setup for your needs
Create an HttpClient
var httpClient = new HttpClient(mockHandler.Object);
It creates an HttpClient instance and pass the above handler to it
Setup an IHttpClientFactory mock
var mockFactory = new Mock<IHttpClientFactory>(MockBehavior.Strict);
mockFactory
.Setup(factory => factory.CreateClient())
.Returns(httpClient)
.Verifiable();
It setups an IHttpClientFactory mock to return the above HttpClient for the CreateClient method call
If you use the IHttpClientFactory to create a named client then change the Setup to this .Setup(factory => factory.CreateClient(It.IsAny<string>()))
Use the mock objects for verification
mockFactory.Verify(factory => factory.CreateClient(), Times.Once);
mockHandler.Protected()
.Verify("SendAsync", Times.Once(), It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>());
I tried as below:
[TestFixture]
public class IndexActionTests
{
private HomeController controller;
[SetUp]
public void Setup()
{
var services = new ServiceCollection();
services.AddHttpClient();
var provider = services.BuildServiceProvider();
var httpclientfactory = provider.GetService<IHttpClientFactory>();
controller = new HomeController(httpclientfactory);
}
[Test]
public void Test1()
{
var result = controller.Index();
Assert.AreEqual(typeof(ViewResult),result.GetType());
}
}
Result:
I would like to call a third party API which provided us two different authorization token values. So we are getting two different sets of results back via invoking the same endpoint URL.
EndpointUrl: https://mail.yahoo.com/
Authorization: Token User123
//Do something with the response for User123
Authorization: Token User345
//Do something with the response for User345
In my client service, my wrapper function should invoke this API by calling it twice with different token values. Get the result and merge it.
Here is my service.
public class MailService : IMailService
{
private readonly HttpClient _httpClient;
public MailService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<UserResponse> GetUserResponse()
{
var uri = new Uri(_httpClient.BaseAddress.AbsoluteUri + "/user-data/");
var response = await _httpClient.GetAsync(uri);
return response;
}
}
I was using Typed Client:
services.AddHttpClient<IMailService,MailService>(client =>
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "User123");
client.BaseAddress = new Uri("https://mail.yahoo.com/");
})
Problem Statement:
What is the best way to retrieve the results? I am not sure if I should create two separate instances of HttpClient? I would like to avoid repetitive code if I go with two different classes containing their own HttpClient.
Maybe my solution lies somewhere in Named Client. I just don't know how to implement that gracefully.
Any help would be appreciated.
You can delegate adding the token header later for each message. Remove the auth header from Startup and add Http message handler (Create a new class "AuthHandler").
builder.Services.AddScoped<AuthHandler>();
services.AddHttpClient<IMailService,MailService>(client => {
client.BaseAddress = new Uri("https://mail.yahoo.com/");
})
.AddHttpMessageHandler<AuthHandler>();
In the AuthHandler, you can add the logic to retrieve and set the auth header.
The override SendAsync method will be called every time a http call is made. Below is a sample code, you can modify as per your logic/requirements:
public class AuthHandler : DelegatingHandler
{
private readonly AppSettings _appSettings;
private static string? _accessToken;
public AuthHandler(IOptions<AppSettings> options)
{
_appSettings = options.Value;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = GetToken();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
return base.SendAsync(request, cancellationToken);
}
private string GetToken()
{
if (_accessToken != null)
{
return _accessToken;
}
_accessToken = "123";// Get your token
return _accessToken;
}
}
What is the best way to retrieve the results?
Named clients are one solution, if you are certain you will always have two authentication headers. That seems very odd to me, though.
SendAsync is another solution. By calling SendAsync instead of GetAsync, you can provide an HttpRequestMessage that can have custom headers. GetAsync is essentially a wrapper around SendAsync for convenience, and you can use the lower-level SendAsync since you need more customization.
I am going to answer, and this is more-or-less opinion based. I like my DI services to be decoupled from everything else in my project. Basically you are putting a configuration to another service in your startup.cs. I like to keep all that stuff in the service that consumes the HttpClient.
So when I inject the IHttpClientFactory, I do it by simply calling:
services.AddHttpClient();
And move on.
Now, in your MailService, you would inject it as so:
public class MailService : IMailService
{
// or store this in your applications external configuration
const Uri BaseUri = new Uri("https://mail.yahoo.com/");
const string UserDataPath = "/user-data/";
private readonly IHttpClientFactory _httpClientFactory;
public MailService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<UserResponse> GetUserResponse(string token)
{
var client = _httpClientFactory.CreateClient();
var uri = new UriBuilder(BaseUri){ Path = UserDataPath }.Uri;
using (var msg = new HttpRequestMessage(HttpMethod.Get, uri))
{
msg.Headers.Authorization = new AuthenticationHeaderValue("Token", token);
using (var resp = await _client.SendAsync(msg).ConfigureAwait(false))
{
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsAsync<UserResponse>().ConfigureAwait(false);
}
}
}
}
Keep in mind that you should use the HttpRequestMessage pattern and not modify any Default... properties of an HttpClient. It could be in use some where else in your application at that time.
Using HttpRequestMessage ensures you get a fresh set of headers that no one else can modify. To sum it up: HttpClient.Headers are not thread-safe.
ETA:
Now that I think about it... that really is your question. You want to use two different headers using one HttpClient. It won't work in the scenario you presented. You would have to have two HttpClient with their own distinct headers.
So, if you don't want to do it the old-school way I presented, you should consider "Named" clients... each configuration has a different name.
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;
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.
I wonder if anyone can help. I have created my own IHttpClient so I am able to mock HttpClient using moq. Works pretty well but there is an internal object called DetaultRequestHeaders which has a property called Authorization.
I am setting this in my constructor or my class that accepts in an injected IHttpClient, of course at runtime everything works as expected. But using Unit Tests I get an error saying
Object reference not set to instance....
This is due to the fact that the DefaultRequestHeaders is null.
Now after inverstigating this further it appears that it is of type
HttpRequestHeaders
but its a sealed class so i can't even do this
.Setup(x => x.DefaultRequestHeaders).Returns(return new HttpRequestHeaders())
Does anyone have any experience in this ?
This is the interface definition
HttpRequestHeaders DefaultRequestHeaders { get; }
Thanks in advance
Just inject the concrete HttpClient and mock the underlying HttpMessageHandler...
http://geekswithblogs.net/abhi/archive/2013/11/20/unit-tests-for-httpclient-using-httpmessagehandler.aspx
You can unit test a class that uses HttpClient by giving that
HttpClient a mock HttpMessageHandler. This way, you can capture the
request and prevent it from actually going over the wire.
Here is an example using Moq. HttpClient depends on
HttpMessageHandler’s SendAsync() method, so give SendAsync() a stub
implementation and use Moq’s Callback() to capture arguments.
var handler = new Mock<HttpMessageHandler>();
handler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Returns(Task<HttpResponseMessage>.Factory.StartNew(() =>
{
return new HttpResponseMessage(HttpStatusCode.OK);
}))
.Callback<HttpRequestMessage, CancellationToken>((r, c) =>
{
Assert.AreEqual(HttpMethod.Get, r.Method);
});
using (var client = new HttpClient(handler.Object))
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://www.google.com");
var response = client.SendAsync(request).Result;
Console.WriteLine(response.StatusCode);
}
HttpRequestHeaders has only internal constructor.
But you can do it by creating HttpRequestMesssage object:
var message = new HttpRequestMessage();
.Setup(x => x.DefaultRequestHeaders).Returns(message.Headers);
Your syntax on the Returns method call appears incorrect. The fact that HttpRequestHeaders is a sealed class shouldn't have any bearing on whether you can return an instance of it from a stubbed method call. Try changing that call to Returns(new HttpRequestHeaders()); or Returns(() => new HttpRequestHeaders());.
If that doesn't work (or the concrete implementation of HttpRequestHeaders is somehow unsuitable for your needs), you might need to try a framework that can mock sealed classes, perhaps Moles or JustMock.