waching
waching

Reputation: 157

Problem mocking delegate with Moq in .Net Core 3.1

I'm having issues mocking a delegate that returns an IHttpClient. This is by requirement of the client, and I cannot change the implementation.

The code looks something like this:

Delegate:

namespace Foo.Http
{
    public delegate IHttpClient FooHttpClientProvider(string name);
}

Test class:

[SetUp]
public void SetUp()
{
    Mock<FooHttpClientProvider>().Setup(x => x("HttpClient")).Returns(Mock<IHttpClient>().Object);
}

When I do this, I get the following exception:

System.InvalidCastException : Unable to cast object of type 'Foo.Http.FooHttpClientProvider' to type 'Moq.IMocked`1[Foo.Http.FooHttpClientProvider]'.

Any ideas? Is this even possible?

Edit: Some extra information: the service I need to test gets injected with a FooHttpClientProvider. We use a global Mock with Autofac that registers the services we need. Since I can't pass the provider as a parameter, I can't instantiate a new Mock for FooHttpClientProvider and pass it to my service as a parameter; I need to set it up in the global mock. This further complicates things.

The code of the service looks something like this:

private readonly FooHttpClientProvider _client;

    public FooService(
        FooHttpClientProvider fooClient)
    {
        
        _client = fooClient("HttpClient");
    }

public async Task<OutputDto> GetAsync(Request request)
{

        var accessToken = await _AuthService.GetAccessTokenAsync();

        try
        {
            var response =
                await ExecuteGet<OutputDto>(
                $"{FooConfig.BaseUrl}someEntpoint{GetQueryParams(request)}", accessToken);
           return response;
        }
        catch(Exception ex)
        {
          //Exception handling
        }
}

The service itself it's working correctly. The problem only arises when trying to mock it for unit testing.

Upvotes: 0

Views: 316

Answers (2)

waching
waching

Reputation: 157

I was able to solve this. Turns out that mocking IHttpClient into my global Mock object, that alone was enough to get carried away to my DI container. Worked like a charm.

Upvotes: 0

Nkosi
Nkosi

Reputation: 247018

This is most likely an XY problem.

No need to mock the delegate. Use the actual delegate that returns a mock IHttpClient

[SetUp]
public void SetUp() {
    Mock<IHttpClient> clientMock = new Mock<IHttpClient>();
    //... set up the client mock to behave as needed
    
    FooHttpClientProvider provider = (string name) => clientMock.Object;
    

    //... use created delegate as needed with the subject under test
    FooService subject = new FooService(provider);
}

Where the subject's constructor is invoked

//...

public FooService(FooHttpClientProvider fooClient) { 
   
    _client = fooClient("HttpClient");

    //...
}

//...

the delegate created in the test will invoke and return the mocked IHttpClient

Upvotes: 1

Related Questions