-1

Update:

Complete code example:

public class DelegatingHandlerStub : DelegatingHandler {
    private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
    public DelegatingHandlerStub() {
        _handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
    }

    public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc) {
        _handlerFunc = handlerFunc;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
        return _handlerFunc(request, cancellationToken);
    }
}

public async Task Should_Return_Ok() {
    //Arrange
    var expected = "Hello World";
    var mockFactory = new Mock<IHttpClientFactory>();
    var configuration = new HttpConfiguration();
    var clientHandlerStub = new DelegatingHandlerStub((request, cancellationToken) => {
        request.SetConfiguration(configuration);
        var response = request.CreateResponse(HttpStatusCode.OK, expected);
        return Task.FromResult(response);
    });
    var client = new HttpClient(clientHandlerStub);
    
    mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(client);
    
    IHttpClientFactory factory = mockFactory.Object;
    
    var controller = new ValuesController(factory);
    
    //Act
    var result = await controller.Get();
    
    //Assert
    result.Should().NotBeNull();
    
    var okResult = result as OkObjectResult;
    
    var actual = (string) okResult.Value;
    
    actual.Should().Be(expected);
}

Original:

I'm following this guide to mock IHttpClientFactory.

https://stackoverflow.com/a/54227679/3850405

For it to work I need the following line:

var configuration = new HttpConfiguration();

enter image description here

Visual Studios fix is Install package 'Microsoft.AspNet.WebApi.Core'.

This works and the code runs fine but I get the following warning:

Warning NU1701 Package 'Microsoft.AspNet.WebApi.Core 5.2.7' was restored using '.NETFramework,Version=v4.6.1, .NETFramework,Version=v4.6.2, .NETFramework,Version=v4.7, .NETFramework,Version=v4.7.1, .NETFramework,Version=v4.7.2, .NETFramework,Version=v4.8' instead of the project target framework 'net5.0'. This package may not be fully compatible with your project.

I have tried to install Microsoft.AspNetCore.Mvc.WebApiCompatShim that was recommended below but it does not work.

https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.WebApiCompatShim

https://stackoverflow.com/a/57279121/3850405

Is there another NuGet that can be used to solve this?

Looking at dependencies for Microsoft.AspNet.WebApi.Core it is only dependent on Microsoft.AspNet.WebApi.Client that in turn uses .NETStandard 2.0. Ideally I would not like to create a new project targeting .NET Standard 2.0 and put the code in there. I would like to use the .NET 5 project.

https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Core/

https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Client/

https://learn.microsoft.com/en-us/dotnet/standard/net-standard

Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • 1
    I don't really follow why you need to use `HttpConfiguration` at all to mock it. That's a type related to ASP.NET MVC WEb API for .NET Framework, you shouldn't need to use it at all in a .NET 5.0 application. – Martin Costello Feb 17 '21 at 08:39
  • @MartinCostello If you have a better solution I'm all ears. – Ogglas Feb 17 '21 at 08:41
  • 1
    If you just need a mock of `IHttpClientFactory` that returns an `HttpClient`, then I'd just create one using Moq, or you could roll your own. It only has one method on it, `HttpClient CreateClient(string)`. https://learn.microsoft.com/en-us/dotnet/api/system.net.http.ihttpclientfactory?view=dotnet-plat-ext-5.0 – Martin Costello Feb 17 '21 at 09:18
  • @MartinCostello I need the request to return an expected result, `Hello World` in the example. – Ogglas Feb 17 '21 at 09:45
  • As so it's more than you need to mock the _IHttpClientFactory__and the _HttpClient_? In that case I'll plug a library I wrote myself for this sort of thing: https://github.com/justeat/httpclient-interception#registering-request-interception-when-using-ihttpclientfactory – Martin Costello Feb 17 '21 at 09:47
  • @MartinCostello I would prefer not to use a third party library since the code works and only shows a warning. – Ogglas Feb 17 '21 at 09:51
  • Hand-roll it yourself then? – Martin Costello Feb 17 '21 at 09:52
  • @Ogglas so you you really need to mock the *results* only. You do that by mocking the HttpClientHandler used by HttpClient. You can specify a mock handler using HttpClientFactory config methods, or by passing one to HttpClient's constructor – Panagiotis Kanavos Feb 17 '21 at 09:52
  • @PanagiotisKanavos Correct, solved it now. – Ogglas Feb 18 '21 at 08:17
  • If you vote down please add a comment why. Very hard to improve questions until next time otherwise. – Ogglas Feb 18 '21 at 08:18

2 Answers2

1

If this is for test purposes I suggest using a stub instead of a mock. CreateClient is an extension method and can't be mocked that easily. Using a stub is much easier.

public class MyHttpClientFactory : IHttpClientFactory
{
    public HttpClient CreateClient(string name)
    {
        return new HttpClient();
    }
}

Depending on your code you might have trouble injecting MyMyHttpClientFactory into your model. But then you should be thinking about refactoring to make it more testable.

Paw Baltzersen
  • 2,662
  • 3
  • 24
  • 33
  • I need the request to return an expected result, `Hello World` in the updated example. – Ogglas Feb 17 '21 at 09:46
  • 1
    @Ogglas which means you need to mock HttpClient, not HttpClientFactory. You do that by mocking the HttpClientHandler used by the HttpClient. In fact, HttpClientFactory has methods to do that already. You could use this stub to return your own HttpClient instances using a stub handler, which you already do – Panagiotis Kanavos Feb 17 '21 at 09:51
1

I did not get HttpConfiguration in .NET 5 so I solved it by removing the need for HttpConfiguration like this:

private LoginController GetLoginController()
{
    var expected = "Hello world";
    var mockFactory = new Mock<IHttpClientFactory>();

    var mockMessageHandler = new Mock<HttpMessageHandler>();
    mockMessageHandler.Protected()
        .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
        .ReturnsAsync(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(expected)
        });

    var httpClient = new HttpClient(mockMessageHandler.Object);

    mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient);

    var logger = Mock.Of<ILogger<LoginController>>();

    var controller = new LoginController(logger, mockFactory.Object);

    return controller;
}
Ogglas
  • 62,132
  • 37
  • 328
  • 418