Stuart Hemming
Stuart Hemming

Reputation: 1663

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?

Upvotes: 12

Views: 10923

Answers (2)

pinkfloydx33
pinkfloydx33

Reputation: 12739

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.

Upvotes: 24

Paddy
Paddy

Reputation: 33857

It seems to be very much as the error reports "Extension methods are not verifiable", in this case, CreateClient is an extension method and cannot have a Verifiable() call tacked onto it.

What are you hoping to test here? Do you really need to verify that CreateClient is called (probably not)? Indeed, do you need to verify that any of these methods are called?

To me, this test should be written to check what the output of the method would be when it gets a 'Bad request' returned. There is no need to 'verify' the calls within the method, as you are starting to really unit test the internal behaviour of the method rather than testing the expected output of that method.

TLDR; Remove the verify calls and change the test to check the expected return behaviour rather than adding hard to maintain checks on internal implementation.

Upvotes: 0

Related Questions