2014-01-11 80 views
7

我是新來的嘲弄。我想嘲笑我的基礎知識庫,這取決於實體框架6 DbContext但我失敗了。我在谷歌搜索了很多,但沒有得到任何足夠的結果。最後,我在testing with async queries上得到了一個例子,並嘗試關注,但它對我有用。如何模擬實體框架6異步方法?

這裏是我的代碼:

的DbContext:

public class TimeSketchContext : DbContext 
{ 
    public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; } 
} 

基礎信息庫:

public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new() 
{ 
    protected readonly DbContext InnerDbContext; 
    protected DbSet<T> InnerDbSet; 

    public BaseRepository(DbContext innerDbContext) 
    { 
     InnerDbContext = innerDbContext; 
     InnerDbSet = InnerDbContext.Set<T>(); 
    } 

    public virtual Task<T> FindAsync(long id) 
    { 
     return InnerDbSet.FirstOrDefaultAsync(x=>x.Id == id); 
    } 

}

測試:

[Fact] 
    public async Task DbTest() 
    { 
     var dummyData = GetEmployeeSkills(); 
     var mockSet = new Mock<DbSet<EmployeeSkill>>(); 

     mockSet.As<IDbAsyncEnumerable<EmployeeSkill>>() 
      .Setup(x => x.GetAsyncEnumerator()) 
      .Returns(new TestDbAsyncEnumerator<EmployeeSkill>(dummyData.GetEnumerator())); 

     mockSet.As<IQueryable<EmployeeSkill>>() 
      .Setup(x => x.Provider) 
      .Returns(new TestDbAsyncQueryProvider<EmployeeSkill>(dummyData.Provider)); 

     mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.Expression).Returns(dummyData.Expression); 
     mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.ElementType).Returns(dummyData.ElementType); 
     mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.GetEnumerator()).Returns(dummyData.GetEnumerator()); 

     var mockContext = new Mock<TimeSketchContext>(); 
     mockContext.Setup(c => c.EmployeeSkill).Returns(mockSet.Object); 

     var baseRepository = new BaseRepository<EmployeeSkill>(mockContext.Object); 

     var data = await baseRepository.FindAsync(1); 

     Assert.NotEqual(null, data); 

    } 

    private EmployeeSkill GetEmployeeSkill() 
    { 
     return new EmployeeSkill 
     { 
      SkillDescription = "SkillDescription", 
      SkillName = "SkillName", 
      Id = 1 
     }; 
    } 

    private IQueryable<EmployeeSkill> GetEmployeeSkills() 
    { 
     return new List<EmployeeSkill> 
     { 
      GetEmployeeSkill(), 
      GetEmployeeSkill(), 
      GetEmployeeSkill(), 
     }.AsQueryable(); 
    } 

結果是:

Assert.NotEqual()失敗

我認爲問題是

public BaseRepository(DbContext innerDbContext) 
{ 
    InnerDbContext = innerDbContext; 
    InnerDbSet = InnerDbContext.Set<T>(); <<<<<<<<<<< 
} 

但不`噸明白爲什麼,以及如何解決這個問題。

我使用:

  • 的Visual Studio 2013旗艦版
  • 起訂量
  • 的xUnit

Thank`s提前。

+1

對未來的讀者:如果你是在所有地方注入DbContext並且無法將其抽象到存儲庫/接口,並且絕對需要「模擬」它,非虛擬化和全部,您可以使用Microsoft Fakes Assemblies來實現此目的,因爲它可以實現一些幻想的IL東西讓你提供一個替代實現甚至一個對象的非虛擬方法。 –

+1

我強烈建議查看一下EF單元測試工具「Effort」(https://effort.codeplex.com/),它可以創建一個足夠真實的EF內存數據庫來測試。我們嘲笑EF也經歷了許多令人頭痛的事情,但在一天結束時,測試EF嘲笑是一個蘋果對桔子的失敗。 (舉一個例子,模擬測試使用L2O而不是L2E。) – RJB

+0

它看起來是一個不錯的工具,稍後會進行檢查。不管感謝@RJB –

回答

8

你是對的問題是在你的InnerDbContext.Set<T>();聲明。

在EF(6.0.2)的DbContext.Set<T> method是的當前版本不virtual所以它不能與起訂量嘲笑。

所以你不能輕易讓你的測試通過,除了通過改變你的BaseRepository的設計不依賴於整個DbContext而是一個DbSet<T>

因此,像:

public BaseRepository(DbSet<T> dbSet) 
{ 
    InnerDbSet = dbSet; 
} 

然後你就可以直接通過你的模擬DbSet。

或者你可以創建一個包裝接口DbContext

public interface IDbContext 
{ 
    DbSet<T> Set<T>() where T : class; 
} 

public class TimeSketchContext : DbContext, IDbContext 
{ 
    public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; } 
} 

然後在你的BaseRepository使用IDbContext

public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new() 
{ 
    protected readonly IDbContext InnerDbContext; 
    protected DbSet<T> InnerDbSet; 

    public BaseRepository(IDbContext innerDbContext) 
    { 
     InnerDbContext = innerDbContext; 
     InnerDbSet = InnerDbContext.Set<T>(); 
    } 

    public virtual Task<T> FindAsync(long id) 
    { 
     return InnerDbSet.FirstOrDefaultAsync(x => x.Id == id); 
    } 
} 

最後,你只需要改變兩行測試,使其通過:

var mockContext = new Mock<IDbContext>(); 
mockContext.Setup(c => c.Set<EmployeeSkill>()).Returns(mockSet.Object); 
+0

Downvoting因爲該方法是'FindAsync',但實現是'FirstAndDefaultAsync'。 – Razort4x