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:
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:
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;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With