Подтвердить что ты не робот

Асинхронные методы возвращают null

Если я попытаюсь высмеять тип, содержащий метод async, например:

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

Затем метод mock Bar возвращает null. Я думаю, что Moq выбирает default(Task<int>) как возвращаемое по умолчанию значение для моего метода, что действительно null. Однако Moq должен скорее выбрать что-то вроде Task.FromResult(default(int)) как значение по умолчанию. Могу ли я заставить Moq использовать методы async, возвращающие ненулевые задачи?

4b9b3361

Ответ 1

Если кто-то заинтересован, я сделал класс расширения, который делает методы асинхронизации более узкими:

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 {  }));
    }
}

Некоторые примеры:

//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

Вам просто нужно заглушить метод "Бар" и вернуть его Task.FromResult(default(int))

Ответ 3

Похоже, эта проблема исправлена ​​ в Moq 4.2. Таким образом, вам просто нужно перейти на новейшую версию Moq (по крайней мере, он начал возвращать непустые задачи в моем случае)

Ответ 4

Напомним, что структура Moq является открытым исходным кодом. В базе кода (доступно здесь), мы видим, что при вызове метода, который не был настроен, возвращаемое значение равно результат закрытого поля valueDel в классе MethodCallReturn<TMock, TResult>. Это поле создается таким образом, чтобы оно возвращало значение по умолчанию для типа результата:

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

Вы можете добавить метод, который будет переопределять значение по умолчанию для заданного типа, возвращаемого из Mock, или явно возвратить предложенный по умолчанию в случае Task.

Вы также можете указать проблему на странице Страница вопросов по выпуску комментариев.

Не имея дело с источником Moq, вы должны просто заглушить интерфейс Foo, как предложил акварага. Быстрое объяснение различия между mocks и заглушками можно найти здесь.