Replace DbContext in WebApplicationFactory for unit testing
Asked Answered
A

3

7

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?

Afrikander answered 7/6, 2021 at 17:52 Comment(0)
A
4

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.
Afrikander answered 8/6, 2021 at 11:12 Comment(0)
C
2

Register DbContext with the Services Container. Open Startup.cs and in the ConfigureServices function, we are going to use the AddDbContext extension method to add our new DbContext and tell it to use SQLite with the connection string from our appsettings.json. The following is the full function with the first two lines being the ones we added.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ContactsDbContext>(options =>
        options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
    services.AddControllers();
    services.AddOpenApiDocument(document => 
        document.PostProcess = d => d.Info.Title = "Contacts API");
}
Clouet answered 8/6, 2021 at 9:25 Comment(0)
K
0

Thanks for guidance. There is no need to remove the context, it suffice with the options class.

var contextOptions = new DbContextOptionsBuilder<MyDbContext>()
            .UseInMemoryDatabase("<name>");
services.AddScoped<DbContextOptions<MyDbContext>>(_ => contextOptions.Options);
Kean answered 7/5 at 20:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.