DbTest.Core 0.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package DbTest.Core --version 0.1.0
                    
NuGet\Install-Package DbTest.Core -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DbTest.Core" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DbTest.Core" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="DbTest.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DbTest.Core --version 0.1.0
                    
#r "nuget: DbTest.Core, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DbTest.Core@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DbTest.Core&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=DbTest.Core&version=0.1.0
                    
Install as a Cake Tool

db-test

Build NuGet License: MIT Sponsors

A .NET library for database integration testing that runs the same test body against SQL Server, PostgreSQL and MySQL.

Every run gets a throwaway database container. Nothing touches a shared, staging or production database.

public abstract class OrderTests : EngineTestBase
{
    protected override string EngineName => "postgres";

    [TestMethod]
    public async Task Total_Should_Sum_The_Customer_Orders()
    {
        await Harness.ExecuteAsync(
            "INSERT INTO customers (email, display_name) VALUES (@email, @name)",
            [new DbParameterValue("@email", "ada@example.test"), new DbParameterValue("@name", "Ada")]);

        var total = await Harness.QuerySingleAsync<int>(
            "SELECT COUNT(*) FROM customers WHERE email = @email",
            [new DbParameterValue("@email", "ada@example.test")]);

        Assert.AreEqual(1, total);
    }
}

[TestClass]
public sealed class PostgresOrderTests : OrderTests { }
[TestClass]
public sealed class MsSqlOrderTests : OrderTests { }
[TestClass]
public sealed class MySqlOrderTests : OrderTests { }

The assertions are written once. No engine name appears in them.

Why

A database test suite has to solve the same handful of problems, and most teams solve them by hand:

Problem What db-test does
Where does the test database come from? A throwaway container per engine, started and destroyed by the harness.
How does the schema get there? Schema files on disk, applied in order, with each engine's own splitting rules.
A test dropped a table and the next test failed on a missing object The shape guard names the class that changed the shape.
One test's rows leaked into the next Clean() empties every table in foreign-key order and resets identity counters.
"The suite is slow" Optional per-test timing output (DBTEST_TIMING=1).
The same assertions written three times The engine sits behind an interface.

Install

dotnet add package DbTest.Core          # the harness
dotnet add package DbTest.MsSql         # SQL Server engine
dotnet add package DbTest.PostgreSql    # PostgreSQL engine
dotnet add package DbTest.MySql         # MySQL engine

Or the CLI:

dotnet tool install --global DbTest.Cli

Requirements

  • Docker must be running. The harness starts real containers through Testcontainers.
  • .NET 8 or newer must be installed. The packages ship a net8.0, a net9.0 and a net10.0 asset, so the runtime you have is the runtime the package was built for. The CLI and the test host also roll forward: a build for .NET 8 runs on .NET 9, 10 and later when the .NET 8 runtime is not installed.

The schema

Point the harness at a directory of SQL files:

await using var harness = await DbTestHarness.StartAsync(
    new PostgreSqlEngine(),
    schemaRoot: "Schema/postgres");

Files are discovered recursively, ordered by path, and applied one after another. Each engine applies them its own way:

SQL Server PostgreSQL MySQL
File format .sql .sql / .psql .sql
Batch separator GO (a client directive the server rejects) none DELIMITER
Function body CREATE PROCEDURE $$ … $$ BEGIN … END
Applied as one batch per GO segment one statement per file one statement per delimiter segment

The splitter tracks string literals, line and block comments, dollar-quoted bodies and DELIMITER directives, so a semicolon inside any of them does not split a statement.

Excluded files are reported, never silently dropped:

var load = SchemaLoader.Load("Schema/postgres", new SchemaLoadOptions
{
    ExcludedDirectories = ["maintenance", "scripts"],
    ExcludedFileNamePatterns = ["^create_", "^fix_"],
    OrderedDirectories = ["tables", "procedures"],
});

foreach (var exclusion in load.Excluded)
{
    Console.WriteLine($"{exclusion.RelativePath} skipped: {exclusion.Reason}");
}

A schema that only partly applied fails loudly at setup rather than surfacing later as unrelated test failures:

var applied = await engine.ApplySchemaAsync(connection, load.Scripts);
applied.ThrowIfFailed();   // throws SchemaApplyException listing every broken file

The shape guard

A shared container means a shared schema. A test that drops or re-types a table leaves the next test failing for the wrong reason — and the failure points at the victim, not the culprit.

The guard snapshots every object after the schema is applied, re-reads it after each test class, and names the class that changed the shape:

[ShapeGuard] OrderTests left dbo.customers columns changed (id,email) -> (id,email,probe)

It detects and blames. It does not repair: a silent repair hides the leak from the next run, and the next run is exactly when you want to see it.

var leaks = await harness.CheckShapeAsync("OrderTests");

Cleanup

await harness.CleanAsync();

Empties every user table in foreign-key order and resets identity counters. The container is discarded afterwards, so this is about isolating tests from each other, not about hygiene.

Diagnostics

DBTEST_TIMING=1 dotnet test

Writes one row per test to dbtest-timing.tsv next to the test binaries. Off by default; no file is written and no query runs unless it is switched on.

The CLI

dbtest engines                                  # list the registered engines
dbtest up --engine postgres --schema Schema     # start a container, apply a schema
dbtest schema --engine mysql --schema Schema    # apply a schema and list what it created

Layout

src/DbTest.Core          engine-agnostic: schema loading, statement splitting,
                         the harness, the shape guard, diagnostics
src/DbTest.MsSql         SQL Server engine
src/DbTest.PostgreSql    PostgreSQL engine
src/DbTest.MySql         MySQL engine
src/DbTest.Cli           the `dbtest` command
tests/DbTest.Integration.Tests
                         one shared test body, run against all three engines

Writing an engine

Implement IDbEngine. Six members: start a container, apply schema, list objects, clean, execute a statement, run a query. Everything else — schema discovery, ordering, exclusion, the shape guard — lives in the core and is shared.

Sponsors

db-test is maintained by dbaitest. Sponsorship pays for the Docker minutes the integration suite burns on every pull request, and for the ADO.NET edge cases — a GO batch inside a stored procedure, a TRUNCATE that a foreign key refuses — that nobody else wants to own.

Sponsor

Contributing

Every change goes through a pull request. See CONTRIBUTING.md for the workflow, the checks that run, and the merge policy.

License

MIT. See LICENSE.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on DbTest.Core:

Package Downloads
DbTest.MySql

MySQL engine for DbTest: container lifecycle, DELIMITER-aware schema application and MySQL introspection.

DbTest.PostgreSql

PostgreSQL engine for DbTest: container lifecycle, dollar-quote-safe schema application and Postgres introspection.

DbTest.MsSql

SQL Server engine for DbTest: container lifecycle, GO-batch schema application and SQL Server introspection.

DbTest.MSTest

MSTest integration for DbTest: a base class that shares one container per engine across the run, checks the shape guard after every test and records per-test timings.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 64 11/8/2026
0.2.0 163 11/8/2026
0.1.0 52 11/7/2026