Search code examples
c#unit-testingentity-framework-coremoq

How to mock up dbcontext?


I am using Entity Framework core 1.0 rc2. Here is the class.

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
        
    }
    public DbSet<Blog> Blogs { get; set; }
}

Then inject the ApplicationDbContext to a class

public class BtnValidator
{
    private readonly ApplicationDbContext _dbContext;
    public BtnValidator(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }
}

Not sure how to mock it in unit test method.

[Fact]
public void Ensure_Proper_Btn_Validated_Return_True()
{
    var dbContext = mockup(ApplicationDbContext); //how

    var validator = new BtnValidator(dbContext);
    var results = validator.IsValid("1234");
    Assure.True(results);
}

EDIT

In BtnValidator, I have code to access the dbContext.

public IsValid(string ID)
{
    var results = _dbContext.Blogs.First(x => x.ID);
    // 
}

Solution

  • You could abstract your DbContext to make it mockable.

    public interface IDbContext {
        DbSet<Blog> Blogs { get; set; }
        //...other properties and members needed for db context
        int SaveChanges();
    }
    
    public class ApplicationDbContext : DbContext, IDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) {
    
        }
    
        public DbSet<Blog> Blogs { get; set; }
    }
    

    You can then inject the contract into dependent classes

    public class BtnValidator {
        private readonly IDbContext _dbContext;
    
        public BtnValidator(IDbContext dbContext) {
            _dbContext = dbContext;
        }
    
        public bool IsValid(string ID) {
            var result = _dbContext.Blogs.FirstOrDefault(x => x.ID == ID);
            return result != null;
        }
    }
    

    and then in your unit tests you can mock the interfaces

    [Fact]
    public void Ensure_Proper_Btn_Validated_Return_True() {
        //Arrange
        var id = "1234"
        var blogsTestData = new List<Blog>(){ new Blog { ID = id } };
        var blogs = MockDbSet(blogsTestData);
        //Set up mocks for db sets
        var dbContext = new Mock<IDbContext>();        
        dbContext.Setup(m => m.Blogs).Returns(blogs.Object);
    
        var validator = new BtnValidator(dbContext.Object);
    
        //Act
        var results = validator.IsValid(id);
    
        //Assert
        Assure.True(results);
    }
    
    Mock<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());
        dbSetMock.Setup(x => x.Create()).Returns(new T());
    
        return dbSetMock;
    }