实体框架6.1中的模拟DbContext

本文关键字:模拟 DbContext 框架 实体 | 更新日期: 2023-09-27 17:59:56

我发现了许多例子,这些例子(显然)展示了用EF 6嘲笑DbContext的一个清晰的工作例子,然而,它们似乎都不适合我,我也不完全确定为什么。

这是我的单元测试代码,用于设置mock;

var mockData = new List<User> { new User { Email = "my@email.com", Id = 1 } }.AsQueryable();
var mockSet = new Mock<DbSet<User>>();
    mockSet.As<IQueryable<User>>().Setup(m => m.Provider).Returns(mockData.Provider);
    mockSet.As<IQueryable<User>>().Setup(m => m.Expression).Returns(mockData.Expression);
    mockSet.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(mockData.ElementType);
    mockSet.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(mockData.GetEnumerator());
    var mockContext = new Mock<MyDbContext>();
    mockContext.Setup(c => c.Users).Returns(mockSet.Object);

然后呼叫我正在测试的服务;

var service = new UsersService(mockContext.Object);
var user = service.GetById(1);

这将引发一个NullReferenceException,因为基础DbSet始终为null。该代码执行以下操作;

在基类中;

public IEnumerable<T> GetAll()
{
    return _dbSet.AsEnumerable();
}

在子类中;

  public User GetById(int id)
        {
            return GetAll().FirstOrDefault(x => x.Id == id);
        }

请注意,尽管SO上还有其他问题似乎相关,但它们不包括EF 6。

作为参考,这是MSDN的一篇文章,其中对相同的代码进行了修改,使其可以编译。

https://msdn.microsoft.com/en-us/data/dn314429.aspx

编辑:

降低了UserService的复杂性(它使用泛型/接口),代码现在很简单;

public User GetById(int id)
        {
            return _dbContext.Set<User>().FirstOrDefault(x => x.Id == id);
        }

如果我将其进一步更改为;

   var dbSet = _dbContext.Set<User>();
        return dbSet.FirstOrDefault(x => x.Id == id);

我可以清楚地看到dbSet为null。

编辑2

根据wablab的建议,mock.Set似乎解决了问题。

还归功于Vladyslav Kushnir为DbSet提供的Generic方法。

为任何可能需要它的人提供工作代码;

 private static Mock<DbSet<T>> GetDbSetMock<T>(IEnumerable<T> items = null) where T : class
        {
            if (items == null)
            {
                items = new T[0];
            }
            var dbSetMock = new Mock<DbSet<T>>();
            var q = dbSetMock.As<IQueryable<T>>();
            q.Setup(x => x.GetEnumerator()).Returns(items.GetEnumerator);
            return dbSetMock;
        }

var mockContext = new Mock<Model1>();
var users = new List<User> { new User { Email = "my@email.com", Id = 1 } };
mockContext.Setup(x => x.Set<User>()).Returns(GetDbSetMock(users).Object);
var service = new UsersService(mockContext.Object);
var user = service.GetById(1);

实体框架6.1中的模拟DbContext

我认为您需要在Set<User>()方法上创建一个设置来返回mock。

    private Mock<DbSet<T>> GetDbSetMock<T>(IEnumerable<T> items = null) where T : class
    {
        if (items == null)
        {
            items = new T[0];
        }
        var dbSetMock = new Mock<DbSet<T>>();
        var q = dbSetMock.As<IQueryable<T>>();
        q.Setup(x => x.GetEnumerator()).Returns(items.GetEnumerator);
        return dbSetMock;
    }

这是我用来模拟DbContext的DbSet的工作得很好的通用方法。此方法的实际调用是:

var contextMock = new Mock<MyContext>();
contextMock.Setup(x => x.MyDbEntities).Returns(GetDbSetMock<MyDbEntity>().Object);