2016-03-16 20 views
1

我製作了一個DbContext的模擬並填充了測試數據。 DbSet是一個受保護的類,所以結果不會被模擬,所以我找到了一個擴展方法。使用C#/ Moq框架輸入參數模擬異步DbContext函數

public static class DbSetMocking 
    { 

    private static Mock<DbSet<T>> CreateMockSet<T>(IQueryable<T> data) 
      where T : class 
    { 
     var queryableData = data.AsQueryable(); 
     var mockSet = new Mock<DbSet<T>>(); 

     mockSet.As<IQueryable<T>>().Setup(m => m.Provider) 
       .Returns(queryableData.Provider); 
     mockSet.As<IQueryable<T>>().Setup(m => m.Expression) 
       .Returns(queryableData.Expression); 
     mockSet.As<IQueryable<T>>().Setup(m => m.ElementType) 
       .Returns(queryableData.ElementType); 
     mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()) 
       .Returns(queryableData.GetEnumerator()); 

     return mockSet; 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, TEntity[] entities) 
     where TEntity : class 
     where TContext : DbContext 
    { 
     var mockSet = CreateMockSet(entities.AsQueryable()); 
     return setup.Returns(mockSet.Object); 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, IQueryable<TEntity> entities) 
     where TEntity : class 
     where TContext : DbContext 
    { 
     var mockSet = CreateMockSet(entities); 
     return setup.Returns(mockSet.Object); 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, IEnumerable<TEntity> entities) 
     where TEntity : class 
     where TContext : DbContext 
    { 
     var mockSet = CreateMockSet(entities.AsQueryable()); 
     return setup.Returns(mockSet.Object); 
    } 
} 

要創建的DbContext的模擬:

var db = new Mock<DbContext>(); 

//Populate 
this.db.Setup(x => x.MyObjects).ReturnsDbSet(
    new List<MyObject> 
    { 
    new MyObject{Id=1, Description="Test"},     
    } 
); 

其次,我試圖延長嘲弄,包括查找(ID)和FindAsync(ID)的方法。 Theese方法正在放置在DbSetMocking類中。查找方法工作正常:

mockSet.Setup(m => m.Find(It.IsAny<object[]>())) 
     .Returns<object[]>(id => StaticMethodtoFindStuff<T>(queryableData, id)); 

但是,我無法使FindAsync方法工作。這是我到目前爲止已經試過:

mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>())) 
    .Returns(Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, 1))); 

這一件作品,但後來我沒有訪問由功能設置的參數。嘗試這一個,編譯工作正常,但它執行失敗,錯誤信息:

類型'System.Object []'的對象不能轉換爲類型'System.Threading.Tasks.Task`1 [系統。目的[]]'。

mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>())) 
    .Returns<Task<object[]>>(d => 
    { 
     return Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d)); 
    }); 

任何人都有一個線索我可以如何實現這個功能?

回答

1

終於搞明白了。原來我在那裏錯過了一個'異步'關鍵字。代碼是:

 mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>())) 
      .Returns<object[]>(async (d) => 
      { 
       return await Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d)); 
      }); 
1

嘗試改變這一行:

mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>())) 
    .Returns<Task<object[]>>(d => 
    { 
     return Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d)); 
    }); 

到:

mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>())) 
    .Returns<object[]>(d => 
    { 
     return Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d)); 
    }); 

的東西在這裏,一般的參數T.Returns<T>不結果的類型,而是,那是的類型第一個參數,在函數中傳入.Setup方法 - 對於你的對象[]。

檢查更多的信息起訂量的源代碼:

https://github.com/moq/moq4/blob/b2cf2d303ea6644fda2eaf10bad43c88c05b395f/Source/Language/IReturns.Generated.cs

下面是從源報價,檢查T1和T2參數評論:

/// <summary> 
/// Specifies a function that will calculate the value to return from the method, 
/// retrieving the arguments for the invocation. 
/// </summary> 
/// <typeparam name="T1">The type of the first argument of the invoked method.</typeparam> 
/// <typeparam name="T2">The type of the second argument of the invoked method.</typeparam> 
/// <param name="valueFunction">The function that will calculate the return value.</param> 
/// <return>Returns a calculated value which is evaluated lazily at the time of the invocation.</return> 
/// <example> 
/// <para> 
/// The return value is calculated from the value of the actual method invocation arguments. 
/// Notice how the arguments are retrieved by simply declaring them as part of the lambda 
/// expression: 
/// </para> 
/// <code> 
/// mock.Setup(x => x.Execute(
///      It.IsAny<int>(), 
///      It.IsAny<int>())) 
///  .Returns((int arg1, int arg2) => arg1 + arg2); //I fixed that line, it's different in the documentation and is incorrect 
/// </code> 
/// </example> 
IReturnsResult<TMock> Returns<T1, T2>(Func<T1, T2, TResult> valueFunction); 

您還可以看到,它定義具有可變數量通用參數的多個返回重載,以便您可以嘲諷具有多達16個參數的方法。