2013-05-09 174 views
17

如果我试图嘲弄包含async方法的类型,如:异步方法返回null

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

然后模拟的Bar方法返回null。我想Moq选择default(Task<int>)作为我的方法的默认返回值,这实际上是null。然而Moq应该选择诸如Task.FromResult(default(int))之类的东西作为默认值。我可以强制Moq使异步方法返回非null任务吗?

回答

33

如果有人有兴趣,我做了一个扩展类,这使得异步方法stubing更简洁:

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; 
+1

史诗般有用。谢谢:-) – 2013-08-01 14:44:04

+0

这工作出色 – Ian1971 2013-10-10 14:55:02

5

你只需要存根的杆法,并使其返回Task.FromResult(default(int))

1

回想一下,起订量框架是开源的。在代码库(可用的here)中,我们可以看到,执行尚未设置的方法调用时,返回值是MethodCallReturn<TMock, TResult>类中的valueDel专用字段的结果。那场被实例化,以便它返回的结果类型的默认值:

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

您可以添加将会覆盖正从一个模拟返回给定类型的默认值的方法,或者明确地回报你在Task的情况下建议使用默认值。

您也可以在Moq issues list page上提出问题。按照aquaraga的建议,如果不处理Moq源代码,那么只需要将Foo接口存根即可。嘲笑和存根之间差异的快速解释可以在here找到。

3

貌似这个问题是起订量4.2 fixed。所以你只需要升级到最新版本的Moq(至少它开始返回非空任务在我的情况)