Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking an HttpClient created using IHttpClientFactory.CreateClient

As the title suggests, I have some code that calls IHttpClientFactory.CreateClient() to create an HttpClient instance.

I'm doing this in .Net Core 3.1

I need to mock this. According to this question"C# Mock IHttpclient & CreateClient" the following should work...

[Test]
public void Mytest() {

    var httpClientFactory = new Mock<IHttpClientFactory>(MockBehavior.Strict);

    httpMessageHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
    httpMessageHandler.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.BadRequest,
        })
        .Verifiable();

    var httpClient = new HttpClient(httpMessageHandler.Object);

    httpClientFactory.Setup(_ => _.CreateClient())   // This fails
        .Returns(httpClient).Verifiable();

    systemUnderTest = new MyService(httpClientFactory.Object);
    var result = systemUnderTest.MyMethod()

    // Assert Stuff
}

However, when I run it, the following is reported...

System.NotSupportedException : Unsupported expression: _ => _.CreateClient() Extension methods (here: HttpClientFactoryExtensions.CreateClient) may not be used in setup / verification expressions.

I'm clearly doing something wrong, but I can't see what it is.

Can anyone offer any pointers?

like image 885
Stuart Hemming Avatar asked Mar 03 '23 08:03

Stuart Hemming


1 Answers

IHttpClientFactory has a single method on it, Create(string). It also has an extension method Create(IHttpClientFactory) that uses the default configuration (it passes Options.DefaultName).

You aren't mocking the interface method, but rather the extension method and as you have realized, mocking extension methods is not possible. But never fear, we have a solution: mock the method that actually appears on the interface!

You can either mock it for all client names, a specific name or the default name (string.Empty):

// any name
httpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())) 
        .Returns(httpClient).Verifiable();

// specific name
httpClientFactory.Setup(_ => _.CreateClient("SpecificName")) 
        .Returns(httpClient).Verifiable();

// the default name (extension method invokes this)
httpClientFactory.Setup(_ => _.CreateClient(string.Empty)) 
        .Returns(httpClient).Verifiable();

The last option matches what happens when the extension method is invoked. But do keep in mind that if you are using named clients that your code may be passing a name to the factory and you'd want to match that.

like image 110
pinkfloydx33 Avatar answered Mar 10 '23 16:03

pinkfloydx33