Async methods return null

百般思念 提交于 2019-11-30 11:28:20

问题


If I try to mock a type containing an async method such as :

interface Foo
{
    Task<int> Bar();
}

Then the mock's Bar method is returning null. I guess Moq is choosing default(Task<int>) as default return value for my method, which is indeed null. However Moq should rather choose something like Task.FromResult(default(int)) as default value. Can I force Moq to make async methods returning non-null Tasks ?


回答1:


If someone is interested, I made an extension class which makes async methods stubing less verbose :

public static class SetupExtensions
{
    public static IReturnsResult<TMock> ReturnsTask<TMock, TResult>(
        this ISetup<TMock, Task<TResult>> setup) where TMock : class
    {
        return setup.Returns(() => Task.FromResult(default(TResult)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, TResult>(
        this ISetup<TMock, Task<TResult>> setup, TResult value) where TMock : class
    {
        return setup.Returns(() => Task.FromResult(value));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, TResult>(
        this ISetup<TMock, Task<TResult>> setup, Func<TResult> func) where TMock : class
    {
        return setup.Returns(Task.Factory.StartNew(func));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, T, TResult>(
        this ISetup<TMock, Task<TResult>> setup, Func<T, TResult> func) where TMock : class
    {
        return setup.Returns<T>(arg => Task.Factory.StartNew(() => func(arg)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, TResult>(
        this ISetup<TMock, Task<TResult>> setup, Func<T1, T2, TResult> func) where TMock : class
    {
        return setup.Returns<T1, T2>((arg1, arg2) => Task.Factory.StartNew(() => func(arg1, arg2)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, T3, TResult>(
        this ISetup<TMock, Task<TResult>> setup, Func<T1, T2, T3, TResult> func) where TMock : class
    {
        return setup.Returns<T1, T2, T3>((arg1, arg2, arg3) => Task.Factory.StartNew(() => func(arg1, arg2, arg3)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, T3, T4, TResult>(
        this ISetup<TMock, Task<TResult>> setup, Func<T1, T2, T3, T4, TResult> func) where TMock : class
    {
        return setup.Returns<T1, T2, T3, T4>((arg1, arg2, arg3, arg4) => Task.Factory.StartNew(() => func(arg1, arg2, arg3, arg4)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock>(this ISetup<TMock, Task> setup, Action action) where TMock : class
    {            
        return setup.Returns(Task.Factory.StartNew(action));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, T>(this ISetup<TMock, Task> setup, Action<T> action) where TMock : class
    {            
        return setup.Returns<T>(arg => Task.Factory.StartNew(() => action(arg)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2>(this ISetup<TMock, Task> setup, Action<T1, T2> action) where TMock : class
    {            
        return setup.Returns<T1, T2>((arg1, arg2) => Task.Factory.StartNew(() => action(arg1, arg2)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, T3>(this ISetup<TMock, Task> setup, Action<T1, T2, T3> action) where TMock : class
    {            
        return setup.Returns<T1, T2, T3>((arg1, arg2, arg3) => Task.Factory.StartNew(() => action(arg1, arg2, arg3)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, T3, T4>(this ISetup<TMock, Task> setup, Action<T1, T2, T3, T4> action) where TMock : class
    {            
        return setup.Returns<T1, T2, T3, T4>((arg1, arg2, arg3, arg4) => Task.Factory.StartNew(() => action(arg1, arg2, arg3, arg4)));
    }

    public static IReturnsResult<TMock> ReturnsTask<TMock>(this ISetup<TMock, Task> setup) where TMock : class
    {
        return setup.Returns(Task.Factory.StartNew(delegate {  }));
    }
}

Some examples :

//Example 1 :
public interface IFoo
{
    Task Bar();
}

var mock = new Mock<IFoo>();

mock.Setup(m => m.Bar()).ReturnsTask(); //await Bar() will return void

//Example 2 :
public interface IFoo
{
    Task<int> Bar();
}

var mock = new Mock<IFoo>();

mock.Setup(m => m.Bar()).ReturnsTask(); //await Bar() will return default(int)

//Example 3 :
public interface IFoo
{
    Task<int> Bar();
}

var mock = new Mock<IFoo>();

mock.Setup(m => m.Bar()).ReturnsTask(4); //await Bar() will return 4;

//Example 4 :
public interface IFoo
{
    Task<int> Bar(int x, int y);
}

var mock = new Mock<IFoo>();

mock.Setup(m => m.Bar(It.IsAny<int>(), It.IsAny<int>()))
                     .ReturnsTask<IFoo, int, int, int>((x,y) => x + y); //await Bar(x, y) will return x + y;



回答2:


You will just have to stub the Bar method, and make it return Task.FromResult(default(int))




回答3:


Looks like this problem is fixed in Moq 4.2. So you just need to upgrade to the newest version of Moq (at least it started returning non-empty Tasks in my case)




回答4:


Recall that the Moq framework is open source. In the code base (available here), we can see that when a method call that has not been set up is executed, the return value is the result of the valueDel private field in the MethodCallReturn<TMock, TResult> class. That field is instantiated so that it returns the default value of the result type:

 private Delegate valueDel = (Func<TResult>)(() => default(TResult));

You could add a method that would override the default value for a given type that is being returned from a Mock, or explicitly return your suggested default in the Task case.

You could also file an issue on the Moq issues list page.

Without dealing with the Moq source, though, you'll have to just stub the Foo interface, as aquaraga suggested. A quick explanation of the difference between mocks and stubs can be found here.



来源:https://stackoverflow.com/questions/16452230/async-methods-return-null

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!