【问题标题】:Mocking DbSet<TEntity> in Repository在存储库中模拟 DbSet<TEntity>
【发布时间】:2017-06-29 13:57:55
【问题描述】:

我想对具有模拟 DbContext 的存储库中的检索方法进行单元测试,但我无法将模拟的 DbSet 值设置到存储库。

存储库如下所示:

public class ChangeLogRepository : Repository<ChangeLog>, IChangeLogRepository
{
    public ChangeLogRepository(IDbContext context, long tenantId) : base(context, tenantId)
    {
    }
}

基类:

public class Repository<TEntity> where TEntity : class {
    protected readonly IDbContext Context;
    protected DbSet<TEntity> Entities { get; set; }
    public long TenantId { get; set; }

    protected Repository(IDbContext context, long tenant)
    {
        Context = context;
        TenantId = tenant;
        Entities = Context.Set<TEntity>();
    }
    public List<TEntity> GetAll()
    {
        return Entities.ToList();
    }
    //..
}

最后但同样重要的是,测试类:

[TestClass]
public class ChangeLogRepository_Test
{
    private ChangeLogRepository repository;
    private List<ChangeLog> allTestData;

    [TestInitialize]
    public void TestInitialize()
    {
        var dbContext = new Mock<IDbContext>();
        allTestData = new List<ChangeLog>() {
            new ChangeLog { Id = 10, EntityName = "User",PropertyName = "UserName",PrimaryKeyValue = 1,OldValue = "Max",NewValue = "Moritz",DateChanged = DateTime.Now,FieldType = ChangeLogFieldType.Default },
            new ChangeLog { Id = 10, EntityName = "User",PropertyName = "CreatedAt",PrimaryKeyValue =2,OldValue = "15/06/2017",NewValue = "15/06/2017",DateChanged = DateTime.Now,FieldType = ChangeLogFieldType.Date },
            new ChangeLog { Id = 10, EntityName = "Role",PropertyName = "RoleName",PrimaryKeyValue = 56,OldValue = "Admin",NewValue = "Administrator",DateChanged = DateTime.Now,FieldType = ChangeLogFieldType.Default },
        };
        var changelogs = MockDbSet(allTestData);
        dbContext.Setup(m => m.Set<ChangeLog>()).Returns(() => changelogs);
        repository = new ChangeLogRepository(dbContext.Object, 10);
    }

    [TestMethod]
    public void Setup_Test()
    {
        Assert.AreEqual(repository.GetAll(), allTestData);
    }
    private static DbSet<T> MockDbSet<T>(IEnumerable<T> list) where T : class, new()
    {
        IQueryable<T> queryableList = list.AsQueryable();
        Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>();
        dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(queryableList.GetEnumerator());
        return dbSetMock.Object;
     }
}

如果我运行它,测试会失败,因为 getAll() 方法返回 null。看起来,模拟的Set() 方法没有正确初始化属性“实体”。

当我在存储库构造器中设置断点时 并检查 Entities 属性,在“Expression > Value > Result View”下出现三个条目。在第一个结果视图下,有一个“枚举未产生结果”消息和两行?在其中(Visual Studio 2017)。

如何正确模拟存储库中的实体?我做错了什么?

【问题讨论】:

    标签: c# entity-framework unit-testing asp.net-core-mvc moq


    【解决方案1】:

    我完全根据原始问题中提供的示例重新创建了测试,但无法重现 null 问题。模拟返回一个填充的集合,就像它配置的那样。

    但是在比较两个集合时出现了问题,

    Assert.AreEqual(repository.GetAll(), allTestData);
    

    他们不被认为是平等的。预期,因为 ToList 将创建一个新列表,这显然是对用作模拟数据源的原始列表的不同引用。

    使用CollectionAssert.AreEquivalent 比较两个集合

    [TestMethod]
    public void Setup_Test() {
        var actual = repository.GetAll();
        CollectionAssert.AreEquivalent(allTestData, actual);
    }
    

    测试通过了。

    【讨论】:

      【解决方案2】:

      EF 文档涵盖了这一点。

      1. 在您的上下文中使用IDbSet&lt;T&gt;
      2. 为您的测试创建 IDbSet&lt;T&gt; 的虚假实现。

      https://msdn.microsoft.com/en-gb/data/dn314431

      请注意,还有一个关于使用模拟框架的页面,但我从未这样做过:https://msdn.microsoft.com/en-gb/data/dn314429

      另请注意,如果使用 EF Core(又名 EF7),则内存中的提供程序完全不需要双精度数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-31
        • 2011-11-17
        • 2019-10-06
        • 1970-01-01
        • 2017-09-10
        • 1970-01-01
        相关资源
        最近更新 更多