2013-05-09 214 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(至少它開始返回非空任務在我的情況)