Course contentsShow
← C#
Lesson 1657 of 1,90815. Software Quality and TestingFree lesson

Testing Entity Framework Core Migrations

Verify that database schema updates apply correctly in an integration context.

In this lesson, you will learn how to verify that your Entity Framework Core migrations correctly transform your database schema within an integration test environment.

Why Test Migrations?

When you evolve your schema, it is easy to assume the DbContext handles everything perfectly. However, complex migrations—like renaming columns, adding non-nullable constraints, or refactoring relationships—can fail due to existing data or engine-specific limitations. Since we are using Testcontainers for our integration tests, we can verify that the migration logic produces the expected state on a real database instance rather than just mocking the behavior.

The Verification Strategy

To test migrations effectively, you should avoid relying on the standard EnsureCreated() method, which simply generates a schema based on your current model state. Instead, you want to verify that the migration history is honored.

The workflow involves:

  1. Spinning up a containerized database.
  2. Running the migrations programmatically.
  3. Querying the database schema to verify the existence of expected columns or constraints.

Here is how you apply the migrations in your test setup:

public async Task Migrations_ShouldApplyCorrectly()
{
    // Arrange: Create a fresh context pointing to the test container
    var context = new MyDbContext(options);

    // Act: Apply pending migrations
    await context.Database.MigrateAsync();

    // Assert: Use EF Metadata to verify the schema matches your expectations
    var databaseCreator = context.Database.GetService<IRelationalDatabaseCreator>();
    var tableExists = await databaseCreator.HasTable("Users");
    
    Assert.True(tableExists);
    
    // Optional: Verify specific column properties
    var entityType = context.Model.FindEntityType(typeof(User));
    var property = entityType.FindProperty("Email");
    Assert.False(property.IsNullable);
}

Avoiding "The Easy Way"

A common pitfall is using EnsureCreated() in tests while using Migrate() in production. By explicitly calling MigrateAsync() in your integration tests, you ensure that the __EFMigrationsHistory table is populated correctly and that your Up() methods behave as expected when run against an actual database engine. This provides the confidence that your production deployment will not crash due to a migration script error.

Key Takeaway: Never assume your migrations work just because your model compiles; programmatically execute MigrateAsync() in your integration tests to ensure the database schema evolves exactly as you intended.