Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic method + func parameter (Cannot convert to type 'Func<NpgsqlConnection,Task<It.IsAnyType>>') #1220

Closed
mateuszloranty opened this issue Dec 1, 2021 · 3 comments
Labels

Comments

@mateuszloranty
Copy link

Hi,
I've studied similar issues like #908 and #919 but still cannot resolve my problem...

This is my method which I want to mock. It's generic and has generic Func<,> parameter:

public async Task<T> OpenAndExecuteAsync<T>(Func<NpgsqlConnection, Task<T>> command)
{
    using var conn = new NpgsqlConnection(_connString);
    await conn.OpenAsync();
    return await command(conn);
}

I want to mock method to avoid body with creating and opening db connection but call Func<,> command
Following the example of #1124, #1001 and similar I tried to mock it this way:

dbConnector
    .Setup(i => i.OpenAndExecuteAsync((Func<NpgsqlConnection, Task<It.IsAnyType>>)It.IsAny<object>()))
    .Returns((Func<NpgsqlConnection,Task<It.IsAnyType>> command) => command(null));

I expect that it should be solve by using the InvocationFunc, but don't know how:

dbConnector
    .Setup(i => i.OpenAndExecuteAsync((Func<NpgsqlConnection, Task<It.IsAnyType>>)It.IsAny<object>()))
    .Returns(new InvocationFunc(invocation =>
    {
        var arg = (Func<NpgsqlConnection, Task<It.IsAnyType>>)invocation.Arguments[0];
        return arg.Invoke(null);
    }));

Still face the same exception:

System.ArgumentException : Object of type 'System.Func`2[Npgsql.NpgsqlConnection,System.Threading.Tasks.Task`1[System.Collections.Generic.IList`1[MyClass]]]' cannot be converted to type 'System.Func`2[Npgsql.NpgsqlConnection,System.Threading.Tasks.Task`1[Moq.It+IsAnyType]]'.

Could anyone give a hint please?

@EklipZgit
Copy link

Also looking for the solution to this same exact issue. My use case is also the same, mocking our SqlProvider to return a mocked SqlConnection in order to mock data at a low level.

@stakx
Copy link
Contributor

stakx commented Jan 23, 2022

    .Returns(new InvocationFunc(invocation =>
    {
        var arg = (Func<NpgsqlConnection, Task<It.IsAnyType>>)invocation.Arguments[0];
        return arg.Invoke(null);
    }));

Using InvocationFunc is going in the right direction. But you're not going far enough. You'll need to convert more of your code in .Returns to use Reflection. No way around it as far as I can see.

It.IsAnyType is a placeholder for some other type, to be used in setups or during verification. But nothing will ever be of that type at runtime, so it doesn't make sense to convert invocation.Arguments[0] to some type involving It.IsAnyType... you'll invariably get a type cast exception.

You've created a setup where all you know about the first argument is that it is a Func<,> accepting an NpgsqlConnection, and returning some Task<>. But you don't know this Task<>'s generic argument, so it is going to be impossible to cast invocation.Arguments[0] and invoking the Func<,>. You'll need to go through Reflection. Something like this (untested):

    .Returns(new InvocationFunc(invocation =>
    {
        var func = invocation.Arguments[0];
        return func.GetType().GetMethod("Invoke").Invoke(obj: dbConnector.Object, parameters: new object[] { null });
    }));

All things considered, you may be better off without It.IsAnyType. Do you really need your setup to be that unspecific?

@stakx stakx added the question label Jan 23, 2022
@stakx
Copy link
Contributor

stakx commented Feb 15, 2022

@mateuszloranty @EklipZgit, does the above provide any help? If I don't hear back from you, I'll close this issue in the next few days.

@stakx stakx closed this as completed Feb 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants