Skip to content

Database Reset for Testing

Weasel provides an FK-aware database cleaner for EF Core that makes integration testing straightforward. Inspired by Respawn and Marten's ResetAllData(), the cleaner discovers tables from your DbContext metadata, resolves foreign key ordering, and generates provider-specific SQL to safely truncate all data.

Setup

Register the cleaner and optional seed data in your DI container:

cs
var builder = Host.CreateDefaultBuilder();
builder.ConfigureServices(services =>
{
    services.AddDbContext<ShopDbContext>(options =>
        options.UseNpgsql("Host=localhost;Database=mydb"));

    // Register the Weasel migrator for your database provider
    services.AddSingleton<Migrator, Weasel.Postgresql.PostgresqlMigrator>();

    // Register the database cleaner
    services.AddDatabaseCleaner<ShopDbContext>();

    // Register seed data (optional, runs after ResetAllDataAsync)
    services.AddInitialData<ShopDbContext, TestOrderSeedData>();
});

snippet source | anchor

Deleting All Data

Use DeleteAllDataAsync() to truncate all tables in FK-safe order (children before parents):

cs
var cleaner = host.Services.GetRequiredService<IDatabaseCleaner<ShopDbContext>>();

// Delete all data from tables managed by the DbContext
// Tables are truncated in FK-safe order (children first)
await cleaner.DeleteAllDataAsync();

snippet source | anchor

Behind the scenes, the cleaner generates provider-specific SQL:

  • PostgreSQL: TRUNCATE TABLE t1, t2, t3 RESTART IDENTITY CASCADE;
  • SQL Server: DELETE FROM each table in dependency order, then DBCC CHECKIDENT to reseed
  • SQLite: DELETE FROM each table in dependency order, then clears sqlite_sequence

Reset with Seed Data

Use ResetAllDataAsync() to delete all data and then run all registered IInitialData<TContext> seeders:

cs
var cleaner = host.Services.GetRequiredService<IDatabaseCleaner<ShopDbContext>>();

// Delete all data, then run all registered IInitialData<T> seeders
await cleaner.ResetAllDataAsync();

snippet source | anchor

IInitialData

Implement IInitialData<TContext> to define seed data applied after a reset. Multiple seeders execute in registration order.

cs
public class TestOrderSeedData : IInitialData<ShopDbContext>
{
    public async Task Populate(ShopDbContext context, CancellationToken cancellation)
    {
        context.Customers.Add(new ShopCustomer { Name = "Test Customer" });
        context.Orders.Add(new ShopOrder { CustomerId = 1, Status = "Pending" });
        await context.SaveChangesAsync(cancellation);
    }
}

snippet source | anchor

Multi-Tenancy

For multi-tenant scenarios where each tenant has its own database, pass an explicit DbConnection to target a specific tenant:

cs
var cleaner = host.Services.GetRequiredService<IDatabaseCleaner<ShopDbContext>>();

// For multi-tenant scenarios, pass an explicit connection
// to target a specific tenant database
await using var tenantConnection = new Npgsql.NpgsqlConnection(
    "Host=localhost;Database=tenant_abc");

await cleaner.DeleteAllDataAsync(tenantConnection);
// or with seeding:
await cleaner.ResetAllDataAsync(tenantConnection);

snippet source | anchor

Performance

The cleaner memoizes the table dependency graph and generated SQL on first use. The EF Core model is immutable, so the SQL never changes. This means subsequent calls in a test suite reuse the cached SQL with zero overhead — critical when running hundreds of tests.

Supported Providers

ProviderStrategyIdentity Reset
PostgreSQLTRUNCATE ... CASCADE RESTART IDENTITYAutomatic
SQL ServerDELETE FROM in FK orderDBCC CHECKIDENT
SQLiteDELETE FROM in FK orderClears sqlite_sequence
MySQLTRUNCATE TABLE with FOREIGN_KEY_CHECKS=0Automatic
OracleDELETE FROM in FK orderN/A (uses sequences)

Released under the MIT License.