1

I am writing unit tests in C# using Moq framework. How can I write a mock statement for the following:

var HttpClient httpClient;

var request = new HttpRequestMessage() { RequestUri = ..., Method = HttpMethod.Post, Content = "..." }
httpClient.Send(request);

How can I mock the last statement calling the Send method? I have already provided dummy content. Since I am writing unit tests, I do not want my test to send an actual request to the URL.

Hence, I would like to mock it and just verify if the Send method was called. What URL should be provided to ensure this behavior, without running into any other issues?

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Keer
  • 25
  • 4
  • An HTTP Request the data is in the body of the request (Content). See : https://stackoverflow.com/questions/58315425/mock-controllerbase-request-using-moq – jdweng Jul 28 '23 at 14:27
  • 1
    You can intercept the request by using a custom HttpMessageHandler. Already asked here https://stackoverflow.com/questions/36425008/mocking-httpclient-in-unit-tests – Kalten Jul 28 '23 at 14:35
  • Thank you for your response. The method I am trying to mock is just Send() not SendAsync(). Will it work for this as well? Also, I am not in a position to modify my original code so will I be able to use this technique without modifying it? I couldn't clearly understand the solution you had referred to. Any clarifications / explanations would be highly appreciated. Thank you. – Keer Jul 28 '23 at 14:52

1 Answers1

0

For the sake of simplicity lets suppose that your to be tested method looks like this:

public class ToBeTestedClass
{
    private readonly HttpClient client;

    public ToBeTestedClass(HttpClient client)
        => this.client = client;

    public HttpResponseMessage ToBeTestedMethod()
    {
        HttpRequestMessage request = new(HttpMethod.Get, "https://httpstat.us/200");
        return client.Send(request);
    }
}

As it was mentioned by Kalten you can mock the underlying HttpMessageHandler to test the HttpClient. In the linked thread the solution used SendAsync but it can be easily adjusted to use Send instead.

public void GivenAFlawlessHttpClient_WhenICallToBeTestedMethod_ThenItInvokesTheHttpClientsSend()
{
    //Arrange
    const string SendMethodName = "Send";
    var handlerMock = new Mock<HttpMessageHandler>();
    handlerMock.Protected()
        .Setup<HttpResponseMessage>(
           SendMethodName, 
           ItExpr.IsAny<HttpRequestMessage>(), 
           ItExpr.IsAny<CancellationToken>())
        .Returns(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent("Dummy response")
        });


    //Act
    var testedClass = new ToBeTestedClass(new HttpClient(handlerMock.Object));
    var response = testedClass.ToBeTestedMethod();

    //Assert
    handlerMock.Protected().Verify(
        SendMethodName,
        Times.Once(),
        ItExpr.Is<HttpRequestMessage>(req =>
            req.Method == HttpMethod.Get
            && req.RequestUri == new Uri("https://httpstat.us/200") 
        ),
        ItExpr.IsAny<CancellationToken>()
    );
}

Please note that you should youse ItExpr instead of It otherwise you will get an ArgumentException.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75