Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock httpclient

I am new in TDD can you please write test case using moq for following code -

public async Task<Model> GetAssetDeliveryRecordForId(string id)
{
    var response = await client.GetAsync($"api/getdata?id={id}");
    response.EnsureSuccessStatusCode();
    var result = await response.Content.ReadAsAsync<Model>();
    return result;
}

Thanks in advance.

like image 910
Satyam Shukla Avatar asked Sep 19 '25 21:09

Satyam Shukla


1 Answers

You can use Moq to mock it.

Using Moq to do that for you

Now, you probably don’t want to create a new class for each response. You could write a helper class for tests which you can prime with whatever response you might want, but that is probably not flexible enough.

Moq is a popular .NET library that helps mocking objects for testing. In essence it uses reflection and expressions to dynamically generate the mocks at runtime during your tests, based on specifications you declare using a fluent API.

Now, there is a slight issue here, too. As you noticed, the SendAsync method on the abstract HttpMessageHandler class is protected. The caveat: Moq can’t auto-implement protected methods as easy as it does with interfaces or public methods. Reason being, that the fluent API uses expressions on the mocked Type, and this can’t offer private or protected members, as you access the class from the outside here. So, we have to use some more advanced features of Moq to mock out our protected method here.

Moq, therefore, has an API for that. You do use Moq. Protected; in your using clauses, and then you can go on on your Moq with the .Protected() method. This gives you some additional methods on the Moq, where you can access the protected members using their names.

A complete test of a class using a HttpClient using Moq would look like this:

// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
   .Protected()
   // Setup the PROTECTED method to mock
   .Setup<Task<HttpResponseMessage>>(
      "SendAsync",
      ItExpr.IsAny<HttpRequestMessage>(),
      ItExpr.IsAny<CancellationToken>()
   )
   // prepare the expected response of the mocked http call
   .ReturnsAsync(new HttpResponseMessage()
   {
      StatusCode = HttpStatusCode.OK,
      Content = new StringContent("[{'id':1,'value':'1'}]"),
   })
   .Verifiable();

// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
   BaseAddress = new Uri("http://test.com/"),
};

var subjectUnderTest = new MyTestClass(httpClient);

// ACT
var result = await subjectUnderTest
   .GetSomethingRemoteAsync('api/test/whatever');

// ASSERT
result.Should().NotBeNull(); // this is fluent assertions here...
result.Id.Should().Be(1);

// also check the 'http' call was like we expected it
var expectedUri = new Uri("http://test.com/api/test/whatever");

handlerMock.Protected().Verify(
   "SendAsync",
   Times.Exactly(1), // we expected a single external request
   ItExpr.Is<HttpRequestMessage>(req =>
      req.Method == HttpMethod.Get  // we expected a GET request
      && req.RequestUri == expectedUri // to this uri
   ),
   ItExpr.IsAny<CancellationToken>()
);

For unit tests, you don’t mock HttpClient. Instead, you mock HttpMessageHandler, put that into an HttpClient and have it return whatever you want that way. If you don’t want to create s specific derivation of HttpMessageHandler for each test, you can also have Moq create the mocks for you automatically.

Read the whole article here.

like image 79
Ali Bahrami Avatar answered Sep 22 '25 10:09

Ali Bahrami