.net-coreentity-framework-coreintegration-testing

The fastest way of applying Entity Framework Core migrations to create a new database


We have ~150 integration tests that run in parallel. To ensure they are isolated, we create a fresh database each time. To create the database we call DatabaseContext.Database.MigrateAsync().

Unfortunately with each additional migration the time to set-up the database increases.

Alternatives I've tried that haven't reduced the total time:

  1. Calling EnsureCreated
  2. Restoring from a .bacpac file
  3. Disabling parallelism in tests and using the Respawn library to reset the shared database

The only success I've had is to delete all the existing migrations and create a single migration in its place. This reduces the total test time significantly - by up to 50%. This feels like a hack though and I don't want to add some additional recurring maintenance step.

What's the fastest way of creating a fresh database for tests?


Solution

  • I've found a simple way to initialise the database without making a call per migration:

    private readonly WebApplicationFactory<Program> _factory;
    
    public async Task InitializeAsync()
    {
        await using AsyncServiceScope scope = _factory.Services.CreateAsyncScope();
        SampleDbContext dbContext = scope.ServiceProvider.GetRequiredService<SampleDbContext>();
        // this is using types normally hidden to application code
        IRelationalDatabaseCreator databaseCreator = dbContext.Database.GetService<IRelationalDatabaseCreator>();
        // needed for idempotency if retrying this method due to transient errors
        await databaseCreator.EnsureDeletedAsync();
        // creates database without schema
        await databaseCreator.CreateAsync();
        // script is not idempotent nor executed in a transaction
        string script = dbContext.Database.GenerateCreateScript().Replace("GO", "");
        await dbContext.Database.ExecuteSqlRawAsync(script);
    }
    

    Things that could be improved:

    1. Using types/methods that are on the public EF API, rather than resolving infrastructure services
    2. Generating an idempotent script so that it can be re-run in case of errors