【问题标题】:async /await unit test with mocked database使用模拟数据库进行异步/等待单元测试
【发布时间】:2014-07-20 03:29:22
【问题描述】:

我有一个方法可以从某个数据库表中获取所有记录:

public async Task<List<T>> GetAllRecordsAsync<T>(EntitiesNew source) where T : class, IGetAllRecords
{
    if (source != null) 
        return await source.Set<T>().ToListAsync();
    return null;
}

我正在尝试编写单元测试。我的测试方法是:

 public async Task GetAllRecordsAsyncTest()
    {
        var data = new List<TABLE_NAME>
        {
            new TABLE_NAME {VALID= 1, NAME = "test 1"},
            new TABLE_NAME {VALID= 1, NAME = "test 2"}
        }.AsQueryable();

        var mockSet = new Mock<DbSet<TABLE_NAME>>();
        mockSet.As<IQueryable<TABLE_NAME>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<TABLE_NAME>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<TABLE_NAME>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<TABLE_NAME>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        var mockContext = new Mock<EntitiesNew>();
        mockContext.Setup(x => x.TABLE_NAME).Returns(mockSet.Object);

        var database = new Database();
        var records = await database.GetAllRecordsAsync<TABLE_NAME>(mockContext.Object);
        int numberOfRecords = records.Count;
        Assert.AreEqual(2, numberOfRecords, "Wrong number of records.");
    }

问题是我从数据库中获得了实际记录数。如何从模拟对象中获取记录数?

【问题讨论】:

    标签: c# unit-testing mocking async-await


    【解决方案1】:

    我认为您可以实现存储库模式来封装数据访问,然后模拟每个存储库,而不是模拟 ORM。模拟会容易得多,因为您只需要模拟 GetAllRecordsAsync&lt;T&gt; 而不是内部。

    你可以尝试做这样的事情:

    interface IRepository<T> where T : class, IGetAllRecords
    {
        Task<List<T>> GetAllRecordsAsync(EntitiesNew source);
    }
    
    public class Repository<T> : IRepository<T> where T : class, IGetAllRecords
    {
        public async Task<List<T>> GetAllRecordsAsync(EntitiesNew source)
        {
            return await Task.FromResult<List<T>>(null);
        }
    }
    
    public class Foo : IGetAllRecords {}
    
    public class FooRepository : Repository<Foo>
    {
    }
    

    我不熟悉您使用的模拟框架,但您可能会像这样模拟IRepository&lt;Foo&gt;

    var mockSet = new Mock<IRepository<Foo>>();
    mockSet.Setup(x => x.GetAllRecordsAsync(null)).Returns(Task.FromResult<List<Foo>>(/*desired return value*/));
    

    【讨论】:

    • 谢谢指点。它给了我方向,解决它的可能方法,但这并不完全是帮助我的。 +1
    【解决方案2】:

    我的猜测是您必须更改 mockContext.Setup(x =&gt; x.TABLE_NAME) 并模拟您用于查询数据的 Set&lt;T&gt;() 函数。

    【讨论】:

    • 你的意思是我需要改变mockContext.Setup(x => x.TABLE_NAME).Returns(mockSet.Object); to mockContext.Setup(x => x.Set()).Returns(mockSet.Object);?
    • 是的。当然,更改您在模拟中返回的类型,对于那段代码,我可以想象模拟应该直接返回数据对象。
    • 我遇到错误 - 非虚拟成员上的设置无效:x.Set()
    • 我已经解决了。谢谢你的想法,它帮助了我。
    【解决方案3】:

    根据this answer,我已经为我的实体类添加了接口:

    public interface IDbContext
    {
        DbSet<T> Set<T>() where T: class; 
    }
    public class EntitiesNew : DbContext, IDbContext
    {
        public EntitiesNew()
            : base("name=EntitiesNew")
        {
        }}
    

    然后我在存储库类中更改了我的方法:

    public async Task<List<T>> GetAllRecordsAsync<T>(IDbContext source) where T : class, IGetAllRecords
    {
        if (source != null) 
            return await source.Set<T>().ToListAsync();
        return null;
    }
    

    最后我的测试方法现在看起来如下:

    [TestMethod]
    public async Task GetAllRecordsAsyncTest()
    {
        var data = new List<TABLE_NAME>
        {
            new TABLE_NAME {VALID= 1, NAME = "test 1"},
            new TABLE_NAME {VALID= 1, NAME = "test 2"}
        }.AsQueryable();
    
        var mockSet = new Mock<DbSet<TABLE_NAME>>();
        mockSet.As<IQueryable<TABLE_NAME>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TABLE_NAME>(data.Provider));
        mockSet.As<IQueryable<TABLE_NAME>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<TABLE_NAME>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<TABLE_NAME>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
        mockSet.As<IDbAsyncEnumerable<TABLE_NAME>>().Setup(x=>x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TABLE_NAME>(data.GetEnumerator()));
        var mockContext = new Mock<IDbContext>();
        mockContext.Setup(x => x.TABLE_NAME).Returns(mockSet.Object);
    
        var database = new Database();
        var records = await database.GetAllRecordsAsync<TABLE_NAME>(mockContext.Object);
        int numberOfRecords = records.Count;
        Assert.AreEqual(2, numberOfRecords, "Wrong number of records.");
    }
    

    【讨论】:

      【解决方案4】:

      另一种选择是使用内存数据库:

      var data = RETURN_DATA;
      var optionsBuilder = new DbContextOptionsBuilder<DB_NAME>()
        .UseInMemoryDatabase(Guid.NewGuid().ToString());
      
      var context = new DB_NAME(optionsBuilder.Options);
        context.TABLE_NAME.Add(data);
        context.SaveChanges();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-01
        • 1970-01-01
        相关资源
        最近更新 更多