当调用两次时,从DbContext访问Moq Mock数据会消失吗?

时间:2018-01-30 09:27:08

标签: c# entity-framework unit-testing mocking moq

我正在尝试了解我的应用程序中发生的行为。我已经嘲笑了DbContext,当我打电话从dbContext.Set<T>().ToList()获取项目时,第二次调用不包含我的模拟数据。我不确定为什么会发生这种情况,因为数据应该仍然存在。请参阅以下代码:

SUT:

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public decimal Salary { get; set; }
}

public class EmployeeDb
    : DbContext
{
    public EmployeeDb()
    {

    }

    public virtual IDbSet<Employee> Employees { get; set; }
}

UNIT TEST:

public class MockDatabase
{
    public Mock<EmployeeDb> SetMockData()
    {
        var mockDb = new Mock<EmployeeDb>();

        mockDb.Setup(i => i.Set<Employee>()).Returns(GetMockSet(Employees).Object);
        mockDb.SetupGet(i => i.Employees).Returns(() => GetMockSet(Employees).Object);

        return mockDb;
    }
    private List<Employee> _providers;
    private List<Employee> Employees => _providers ?? (_providers = new List<Employee>
    {
        GetEmployee(1),
        GetEmployee(2),
        GetEmployee(3),
        GetEmployee(4),
        GetEmployee(5),
    });
    private static Employee GetEmployee(int id)
    {
        return new Employee
        {
            FirstName = Faker.Name.First(),
            LastName = Faker.Name.Last(),
            Age = Faker.RandomNumber.Next(50),
            Id = id,
            Salary = Faker.RandomNumber.Next(100000)
        };
    }

    #region Hood

    public static Mock<DbSet<T>> GetMockSet<T>(IList<T> items) where T : class
    {
        var querable = items.AsQueryable();
        var mockSet = new Mock<DbSet<T>>();
        mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(querable.Provider);
        mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(querable.Expression);
        mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(querable.ElementType);
        mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(querable.GetEnumerator());
        mockSet.Setup(i => i.Add(It.IsAny<T>())).Callback(delegate (T item) {
            items.Add(item);
        });
        return mockSet;
    }

    #endregion
}


[TestClass]
public class UnitTest1
{
    private EmployeeDb _context;

    [TestInitialize]
    public void TestInitialize()
    {
        var mockDb = new MockDatabase();
        _context = mockDb.SetMockData().Object;
    }
    [TestMethod]
    public void Test_CallTwice_ReturnsEqualCount()
    {
        var emps = _context.Set<Employee>().ToList();
        var emps2 = _context.Set<Employee>().ToList();

        Assert.IsTrue(emps.Count == emps2.Count);

        // -- This works
        //var empCount = _context.Set<Employee>().Count();
        //var empCount2 = _context.Set<Employee>().Count();

        //Assert.IsTrue(empCount == empCount2);
    }
}

我有没有得到关于此代码的内容? Moq对ToList()有什么看法吗?

1 个答案:

答案 0 :(得分:2)

ToList在调用时枚举集合,但枚举器仅为前向,因此在第一次调用之后它已经在最后。

当设置GetEnumerator时,使用Returns的函数重载以允许多次调用,每次都会返回相同的枚举数,并且您获得了所经历的行为。

mockSet.As<IQueryable<T>>()
    .Setup(m => m.GetEnumerator())
    .Returns(() => querable.GetEnumerator()); //<-- function