Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

enter image description here

like image 506
chandra sekhar Avatar asked Jul 18 '19 09:07

chandra sekhar


2 Answers

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:

  1. Unit Testing with the HttpClient
  2. How to mock HttpClient in your .NET / C# unit tests
like image 89
Super Jade Avatar answered Oct 12 '22 12:10

Super Jade


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;
    });
like image 29
Panagiotis Kanavos Avatar answered Oct 12 '22 12:10

Panagiotis Kanavos