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

It.IsAnyType not working for Func returning Task with generic type #1040

Closed
Ansssss opened this issue Jul 16, 2020 · 1 comment
Closed

It.IsAnyType not working for Func returning Task with generic type #1040

Ansssss opened this issue Jul 16, 2020 · 1 comment

Comments

@Ansssss
Copy link

Ansssss commented Jul 16, 2020

I believe this is a bug. Originally posted on stackoverflow.

Note this seems very close to the issue 918, but I see that one is closed and I'm still seeing this issue with Moq version 4.14.0.0.

I've tried 3 different ways to setup a mock for a generic interface method. Only one way works, but it is using an explicit type, so won't work generically. I tried using It.IsAnyType, but it doesn't seem to match on the call that is made.

Here is the sample code (I expected test case 1 to have "asdf" returned). How can I get the mock to work for any type (not just string?)?

using Moq;
using System;
using System.Threading.Tasks;

namespace blah
{
    public class Junk : IGetOrAddable
    {
        public static void Main()
        {
            Test(1);
            Test(2);
            Test(3);
            /*********OUTPUT*********
            For test case 1, value is null
            For test case 2, value is asdf
            Unhandled exception. System.ArgumentException: Object of type 
                'System.Func`2[System.Object,System.Threading.Tasks.Task`1[System.String]]'
                cannot be converted to type 
                'System.Func`2[System.Object,System.Threading.Tasks.Task`1[Moq.It+IsAnyType]]'.
            *************************/
        }

        public static void Test(int testCase)
        {
            Mock<IGetOrAddable> mock = new Mock<IGetOrAddable>();

            //setup the mock to always call the valueFactory function (ignore cache)
            switch(testCase)
            {
                case 1:
                {
                    //use the It.IsAnyType to match any generic invocation of this method
                    mock.Setup(x => x.GetOrAdd<It.IsAnyType>(It.IsAny<object>(), It.IsAny<Func<object, Task<It.IsAnyType>>>()))
                        .Returns((object k, Func<object, Task<It.IsAnyType>> f) => f(k));
                    break;
                }
                case 2:
                {
                    //use an exact type (string?) to match a specific type invocation of this method
                    mock.Setup(x => x.GetOrAdd<string?>(It.IsAny<object>(), It.IsAny<Func<object, Task<string?>>>()))
                        .Returns((object k, Func<object, Task<string?>> f) => f(k));
                    break;
                }
                case 3:
                {
                    //try casting It.IsAny<object> per this suggestion: https://stackoverflow.com/a/61322568/352349
                    mock.Setup(x => x.GetOrAdd<It.IsAnyType>(It.IsAny<object>(), (Func<object, Task<It.IsAnyType>>)It.IsAny<object>()))
                        .Returns((object k, Func<object, Task<It.IsAnyType>> f) => f(k));
                    break;
                }
            }
            
            var value = mock.Object.GetOrAdd<string?>(new object(), RetrieveCoolValue).Result;
            Console.WriteLine($"For test case {testCase}, value is {value ?? "null"}");
        }
        public Task<T> GetOrAdd<T>(object key, Func<object, Task<T>> valueFactory)
        {
            //complicated cache retrieval stuff here of the sort described in https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.getoradd?view=netcore-3.1
            throw new NotImplementedException();
        }
        public static Task<string?> RetrieveCoolValue(object key)
        {
            return Task.FromResult<string?>("asdf");
        }
    }

    public interface IGetOrAddable
    {
        Task<T> GetOrAdd<T>(object key, Func<object, Task<T>> valueFactory);
    }
}
@stakx
Copy link
Contributor

stakx commented Aug 1, 2020

Pretty sure this is a duplicate of #919, given that your example uses It.IsAnyType as a nested part of another type (Task<It.IsAnyType>), support for which isn't implemented yet (like those other issues mention).

See the linked issues for suggestions how to work around this current limitation.

@stakx stakx added the duplicate label Aug 1, 2020
@stakx stakx closed this as completed Sep 27, 2020
@devlooped devlooped locked and limited conversation to collaborators Sep 4, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants