How to Inject and Mock DB Context with DB Context Options in Entity Framework Core Using InMemoryDatabase




Hello Friends, Today let's see how we can inject a db context through the start up class and how to mock the db context in an Entity Framework core application.

Suppose we have a db context with a dbset 'Users' as following which is to be created using the db context options.

public class MyDbContext : DbContext
{ 
    public MyDbContext (DbContextOptions options) : base (options)
    { 

    }


    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>(eb =>
        {
            // Setup db table
        });
    }
}


Now let's see how we can inject the db context from the start up class with the db context options. In here we have the code to setup and inject the configuration class too (we are reading the connection string for the db context using the configuration object)

public class Startup 
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        var configuration = new Configuration(new ConfigurationBuilder()
                                .SetBasePath(Environment.CurrentDirectory)
                                .AddJsonFile("local.settings.json"
                                    , optional: true, reloadOnChange: true)
                                .AddEnvironmentVariables()
                                .Build());

        builder.Services.AddSingleton<Configuration>((s) => {
            return configuration;
        });
        
        builder.Services.AddDbContext<MyDbContext>(options 
                            => options.UseSqlServer(configuration.GetConnectionString("MyDbConnectionString")));
    }
}



Now Let's see how we can mock db context with Users db set while writing unit tests.First we will have to install the InMemory package by executing the following command in the vs code terminal.

PS D:\SampleTest> Install-Package Microsoft.EntityFrameworkCore.InMemory


Now the following code will create a db context option with InMemoryDatabase setup, db context will be created using the db context option and an Mock user db set will be created from a dummy users list.

// Create mock DB options
var dbOptions = new DbContextOptionsBuilder<MyDbContext>()
                    .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
                    .Options;

// Create mock DB Context
var myDbContext = new MyDbContext(dbOptions);

// Create user list
var userslist = new List<User>()
{    
    new User()
    {
        FirstName = "test first name",
        LastName = "test last name",
    },
    new User()
    {
        FirstName = "test first name 2",
        LastName = "test last name 2",
    }
}

// Add list into the user db set in the db context
var queryable = userslist.AsQueryable();

var dbSet = new Mock<DbSet<User>>();
dbSet.As<IQueryable<User>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<User>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());

myDbContext.Users = dbSet.Object;


Now let's standardize the code by restructuring it in a way which will make it easily reusable in other classes. For that we will create a helper class where the db context and mock db set creation codes will be moved into it. The db set creation method will be written as generic so that it can be used for mocking other db set classes as well.

public static class DbContextMockHelper
{
    public static MyDbContext GetDbContext()
    {
        var dbOptions = new DbContextOptionsBuilder<MyDbContext>()
                            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
                            .Options;
        
        return new My2DbContext(dbOptions);
    }

    public static DbSet<T> GetQueryableMockDbSet<T>(params T[] sourceList) where T : class
    {
        var queryable = sourceList.AsQueryable();

        var dbSet = new Mock<DbSet<T>>();
        dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
        dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
        dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
        dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());

        return dbSet.Object;
    }
}


Now let's see how we can use the helper class from a test class.

[TestClass]
public class UserTest
{   
    private MyDbContext _dbContext;
    
    [TestInitialize]
    public void Initialize()
    {
        _dbContext = GetDbContextWithUsersData();
    }

    [TestMethod]
    public async Task UserInfoTest()
    {
        // Test method
    }

    private static MyDbContext GetDbContextWithUsersData()
    {
        var dbContext = DbContextMockHelper.GetDbContext();
        dbContext.Users = DbContextMockHelper.GetQueryableMockDbSet(
            new User()
            {
                FirstName = "test first name",
                LastName = "test last name",
            },
            new User()
            {
                FirstName = "test first 2",
                LastName = "test last 2",
            }
        );

        return dbContext;
    }
}


This way you can easily mock all other db sets in your db context.





Enjoy coding...!!!


Comments