How do I go about unit testing with Entity Framework and Moq?

徘徊边缘 提交于 2019-12-03 08:20:09
Nkosi

You just need to create a collection to act as the backing store and mock the enumeration db set with the backing collection

public class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class {
    public MockDbSet(List<TEntity> dataSource = null) {
        var data = (dataSource ?? new List<TEntity>());
        var queryable = data.AsQueryable();

        this.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider);
        this.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression);
        this.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType);
        this.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(() => queryable.GetEnumerator());
        //Mocking the insertion of entities
        this.Setup(_ => _.Add(It.IsAny<TEntity>())).Returns((TEntity arg) => {
            data.Add(arg);
            return arg;
        });

        //...the same can be done for other members like Remove
    }
}

So now you can use a list to hold the data

// ARRANGE
var dataSource = new List<User>(); //<-- this will hold data
var user = new User()
{
    FirstName = "Some",
    LastName = "Guy",
    EmailAddress = "some.guy@mockymoqmoq.com",
};

var mockSet = new MockDbSet<User>(dataSource);
var mockContext = new Mock<WebAPIDbContext>();

mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object);

// ACT
using (var uow = UnitOfWork.Create(mockContext.Object))
{
    uow.UserRepository.Add(user);
    uow.SaveChanges();


    // ASSERT
    mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once());
    Assert.IsTrue(dataSource.Contains(user)); //<-- shows mock actually added item
    Assert.IsTrue(uow.UserRepository.Any(u => u == user)); //<-- show you can actually query mock DbSet
}