Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace DbContext in WebApplicationFactory for unit testing

I need to replace context in WebApplicationFactory. I have MyDbContext which I want to replace with SQLite context for testing.

The replace part works fine

.ConfigureServices(services =>
  {
    // Remove the app's ApplicationDbContext registration.
    var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<MyDbContext>));

    if (descriptor != null)
    {
      services.Remove(descriptor);
    }
    
    services.AddDbContext<MyDbContext>(builder =>
    {
      builder.UseSqlite(CreateInMemoryDatabase());
    });
  });

But because I moved from Npgsql to SQLite in tests I need to override some default values in OnModelCreating. I created a new db context class

public class MySqlLiteDbContext: MyDbContext
    {
        public MySqlLiteDbContext(DbContextOptions<MyDbContext> options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Record>()
                .Property(b => b.DateCreated)
                .HasDefaultValueSql("(datetime('now'))");

            ...
        }
    }

Is there any way to inject MySqlLiteDbContext instead of MyDbContext to force EnsureCreated use OnModelCreating for SQLite? Is extracting IMyDbContext an option? What else can I do to solve this problem?

like image 967
xakpc Avatar asked Aug 02 '26 10:08

xakpc


1 Answers

Ok, looks like I managed to solve it like this

  1. Add non-generic DBContext constructor override, make it protected
public class MyDbContext: DbContext {        
    public MyDbContext(DbContextOptions<MyDbContext> options): base(options) {}
    // new
    protected MyDbContext(DbContextOptions options): base(options) {}
        
    // rest context stuff...
}
  1. Remove DBContext and DBContextOptions in WebApplicationFactory on ConfigureServices
.ConfigureServices(services => {
    // Remove the app's ApplicationDbContext registration.
    var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof (DbContextOptions<MyDbContext>));

    if (descriptor != null) {
        services.Remove(descriptor);
    }

    var descriptor2 = services.SingleOrDefault(d => d.ServiceType == typeof (MyDbContext));

    if (descriptor2 != null) {
        services.Remove(descriptor2);
    }

    //...
})
  1. Add your descendant context as implementation
.ConfigureServices(services => {
    // Remove the app's ApplicationDbContext registration.

    // ...
    services.AddDbContext<MyDbContext, MySqliteDbContext>(builder => {
        builder.UseSqlite(CreateInMemoryDatabase());
    }, ServiceLifetime.Singleton);
})

Note: If you use InMemory Sqlite consider Singleton scope.

  1. Done. Now when DocumentComparisonContext injected DocumentComparisonSqlLiteContext will be used as implementation, and on EnsureCreated sqlite-specific logic will be used.
like image 91
xakpc Avatar answered Aug 05 '26 11:08

xakpc