How to mock up dbcontext?

12,398

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;
}
Share:
12,398
Undo
Author by

Undo

Stack Overflow moderator. I bite, but only when sufficiently provoked. Everything I post on Stack Overflow is licensed to you under CC0: you can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. Feel free to just throw it into your project without attribution. That's what most folks do anyway... but for my contributions, it's nice and legal.

Updated on June 07, 2022

Comments

  • Undo
    Undo almost 2 years

    I am using Entity Framework 7 in .net 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);
        // 
    }
    
    • Evk
      Evk almost 8 years
      It's not a mock, but consider using in-memory provider for EF (Effort - effort.codeplex.com). Good for unit-testing EF-related code.
    • Admin
      Admin almost 8 years
      @Evk, there are two things. One is I need unit test, Secondly I use .Net core 1.0 rc2, the library perhaps doesn't support it.
    • Crowcoder
      Crowcoder almost 8 years
      Maybe it is not practical for you at this point but consider using a repository pattern. Then you mock the repo, not the actual db access.
    • RBT
      RBT almost 8 years
      @Crowcoder We use unit of work pattern which wraps the repositories behind the scene corresponding to various tables of my database. For testing scenarios we have a separate class in the name of InMemoryUnitOfWork which acts in place of real UnitOfWork class to manage all the repositories in-memory. In-memory repositories have no dependencies on real database.
  • farizmamad
    farizmamad over 2 years
    Great idea. If I'm not mistaken, Mock is provided in Moq Nuget package. However, in my case x.Create() method doesn't found. I installed Moq v4.16.1