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 });
}
}
Related
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.
I have a policy that looks like this
var retryPolicy = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(resp => resp.StatusCode == HttpStatusCode.Unauthorized)
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (resp, timeSpan, context) =>
{
// not sure what to put here
});
Then I have a named client that looks like this
services.AddHttpClient("MyClient", client =>
{
client.BaseAddress = new Uri("http://some-url.com");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
client.Timeout = 30000;
})
.AddPolicyHandler(retryPolicy);
I need to refresh the bearer token on the http client in the event I receive a 401. So in a perfect world the following code would do exactly what I'm trying to accomplish
var retryPolicy = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(resp => resp.StatusCode == HttpStatusCode.Unauthorized)
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (resp, timeSpan, context) =>
{
var newToken = GetNewToken();
//httpClient doesn't exists here so I need to grab it some how
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", newToken);
});
I have read the following articles:
Re-establishing authentication using Retry
Refresh Token using Polly with Typed Client
retry-to-refresh-authorization
and a couple others as well. However, they all seem use policy.ExecuteAsync() which I don't want to use because then I would have to change all the HttpClient calls throughout my solution. I'm trying to find a way to simply add this functionality to every request by only changing code in the StartUp.cs.
TL;DR: You need to define a communication protocol between a RetryPolicy, a DelegatingHandler and a TokenService.
In case of Typed Clients you can explicitly call the ExecuteAsync and use the Context to exchange data between the to-be-decorated method and the onRetry(Async) delegate.
This trick can't be used in a named client situation. What you need to do instead:
Separate out the Token management into a dedicated service
Use a DelegatingHandler to intercept the HttpClient's communication
This sequence diagram depicts the communication between the different components
Token Service
The DTO
public class Token
{
public string Scheme { get; set; }
public string AccessToken { get; set; }
}
The interface
public interface ITokenService
{
Token GetToken();
Task RefreshToken();
}
The dummy implementation
public class TokenService : ITokenService
{
private DateTime lastRefreshed = DateTime.UtcNow;
public Token GetToken()
=> new Token { Scheme = "Bearer", AccessToken = lastRefreshed.ToString("HH:mm:ss")};
public Task RefreshToken()
{
lastRefreshed = DateTime.UtcNow;
return Task.CompletedTask;
}
}
The registration into the DI as Singleton
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITokenService, TokenService>();
...
}
Delegating Handler
The custom exception
public class OutdatedTokenException : Exception
{
}
The handler (interceptor)
public class TokenFreshnessHandler : DelegatingHandler
{
private readonly ITokenService tokenService;
public TokenFreshnessHandler(ITokenService service)
{
tokenService = service;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = tokenService.GetToken();
request.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, token.AccessToken);
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new OutdatedTokenException();
}
return response;
}
}
It retrieves the current token from the TokenService
It sets the authorization header
It executes the base method
It checks the response's status
If 401 then it throws the custom exception
If other than 401 then it returns with the response
The registration into the DI as Transient
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITokenService, TokenService>();
services.AddTransient<TokenFreshnessHandler>();
...
}
Retry Policy
The policy definition
public IAsyncPolicy<HttpResponseMessage> GetTokenRefresher(IServiceProvider provider)
{
return Policy<HttpResponseMessage>
.Handle<OutdatedTokenException>()
.RetryAsync(async (_, __) => await provider.GetRequiredService<ITokenService>().RefreshToken());
}
It receives an IServiceProvider to be able to access the TokenService
It performs a single retry if an OutdatedTokenException was thrown
Inside the onRetryAsync delegate it calls the TokenService's RefreshToken method
Putting all things together
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITokenService, TokenService>();
services.AddTransient<TokenFreshnessHandler>();
services.AddHttpClient("TestClient")
.AddPolicyHandler((provider, _) => GetTokenRefresher(provider))
.AddHttpMessageHandler<TokenFreshnessHandler>();
...
}
Please bear in mind that the ordering of AddPolicyHandler and AddHttpMessageHandler matters
If you would call the AddHttpMessageHandler first and then the AddPolicyHandler in that case your retry would not be triggered
This post contains an alternative version of my previously suggested solution.
I'm posting this as a separate answer (rather than editing the previous one) because both solutions are viable and the other post is already a lengthy one.
Why do we need an alternative version?
Because the TokenFreshnessHandler has too much responsibility whereas the Retry policy has too few.
If you look at the SendAsync method overridden implementation then you can see that it perform some operation on the request and on the response as well.
If we could make a separation where
the handler deals only with the request
and policy make its assessment on the response
then we would end up with a much cleaner solution (IMHO).
How can we achieve this separation?
If we could use the Polly's Context as an intermediate storage between the retry attempts then we were able to do this separation. Fortunately the Microsoft.Extensions.Http.Polly package defines two extension methods against the HttpRequestMessage:
SetPolicyExecutionContext
GetPolicyExecutionContext
These are under-documented features. On the docs.microsoft I could not even find the related pages. I have only found them under the dotnet-api-docs repo.
These can be useful if we know that the AddPolicyHandler attaches a new Context to the request only if it did not have one already. Unfortunately, this is yet again not documented, so it is an implementation detail which might change in the future. But currently we can rely on this.
How will this change the protocol?
As you see only difference here is the usage of the Context.
How should we change the handler?
public class TokenRetrievalHandler : DelegatingHandler
{
private readonly ITokenService tokenService;
private const string TokenRetrieval = nameof(TokenRetrieval);
private const string TokenKey = nameof(TokenKey);
public TokenRetrievalHandler(ITokenService service)
{
tokenService = service;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var context = request.GetPolicyExecutionContext();
if(context.Count == 0)
{
context = new Context(TokenRetrieval, new Dictionary<string, object> { { TokenKey, tokenService.GetToken() } });
request.SetPolicyExecutionContext(context);
}
var token = (Token)context[TokenKey];
request.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, token.AccessToken);
return await base.SendAsync(request, cancellationToken);
}
}
I've changed the name of the handler since its responsibilities have changed
Now, the handler's implementation only cares about the request (and does not care about the response)
As it was said previously: the PolicyHttpMessageHandler creates a new Context if there wasn't any
Because of this the GetPolicyExecutionContext does not return null (even for the very first attempt) rather than a Context with an empty context data collection (context.Count == 0)
How should we change the policy?
public IAsyncPolicy<HttpResponseMessage> GetTokenRefresher(IServiceProvider provider, HttpRequestMessage request)
{
return Policy<HttpResponseMessage>
.HandleResult(response => response.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(async (_, __) =>
{
await provider.GetRequiredService<ITokenService>().RefreshToken();
request.SetPolicyExecutionContext(new Context());
});
}
Rather than triggering the policy for a custom exception, now it triggers in case of 401 response's status code
The onRetryAsync has been modified in the way that it clears the attached context of the request
The registration code should be adjusted as well
services.AddHttpClient("TestClient")
.AddPolicyHandler((sp, request) => GetTokenRefresher(sp, request))
.AddHttpMessageHandler<TokenRetrievalHandler>()
Now, we should pass to the GetTokenRefresher method not just the IServiceProvider but also HttpRequestMessage as well
Which solution should I use?
This solution offers nicer separation but it relies on an implementation detail
The other solution makes the handler smart whereas the policy dumb
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);
}
}
I am writing a client library for an API that provides both public and authenticated endpoints. I would like to easily denote which endpoints require authentication using attributes. For instance:
public async Task<ApiResponse> GetPublicData()
{
var request = CreateRequest( "v1/public" );
return await _httpClient.GetAsync( request );
}
[RequiresAuthentication]
public async Task<ApiResponse> GetPrivateData()
{
var request = CreateRequest( "v1/private" );
return await _httpClient.GetAsync( request );
}
private ApiRequest CreateRequest( string endpoint )
{
var request = new ApiRequest( endpoint );
// if (caller has RequiresAuthenticationAttribute)
// SignRequest( request, _credentials );
return request;
}
As far as I am aware, the only way I can access whether or not the caller of CreateRequest has a RequiresAuthenticationAttribute is to create a stack frame, find the method via reflection, and then attempt to get the attribute from the MethodInfo. This can be incredibly slow.
Is there any other way to pass this information into the callee that won't kill performance. I know that the requests will always be limited by the response time of the API, but given that it has to do with financial data, being able to fire off requests as soon as possible is a must, and being able to do it in a clean way that involves attributes instead of manually passing parameters would be very nice.
You could try using the CallerMemberNameAttribute class.
The attributes "Allows you to obtain the method or property name of the caller to the method."
private ApiRequest CreateRequest(string endpoint, [CallerMemberName] string callerMemberName= "")
{
var methodInfo = this.GetType().GetMethod(callerMemberName);
var attributes = (RequiresAuthenticationAttribute)method.GetCustomAttributes(typeof(RequiresAuthenticationAttribute), true);
var request = new ApiRequest( endpoint );
if (attributes.Any())
SignRequest(request, _credentials);
return request;
}
If you are set on using attributes, then you are going to have to use Reflection in one way or another. Some reflection mechanisms are faster than others, but there is still a runtime penalty which you will have to pay. On the other hand, if what you want is a separation of concerns (and using attributes is not a given), then you might want to think about using interfaces to separate those concerns.
For example:
public interface IAuthenticated
{
public async Task<ApiResponse> GetPrivateData();
}
public interface IPublicAccess
{
public async Task<ApiResponse> GetPublicData();
}
public async Task<ApiResponse> IPublicAccess.GetPublicData()
{
var request = CreateRequest( "v1/public" );
return await _httpClient.GetAsync( request );
}
public async Task<ApiResponse> IAuthenticated.GetPrivateData()
{
var request = CreateRequest( "v1/private" );
return await _httpClient.GetAsync( request );
}
private ApiRequest CreateRequest( string endpoint )
{
var request = new ApiRequest( endpoint );
// if (caller has RequiresAuthenticationAttribute)
// SignRequest( request, _credentials );
return request;
}
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.