db-test
db-test is a .NET library for database integration testing. You write a test body once and run it against SQL Server, PostgreSQL and MySQL.
Every run gets a throwaway database container. Nothing touches a shared, staging or production database.
What a test looks like
Section titled “What a test looks like”The tests live in an abstract class. No engine name appears in them.
public abstract class CustomerTests : DbTestBase{ [TestMethod] public async Task Inserted_Customer_Should_Be_Counted() { await Harness.CleanAsync(); await Harness.ExecuteAsync( "INSERT INTO customers (email, display_name) VALUES (@email, @name)", [new DbParameterValue("@email", "ada@example.test"), new DbParameterValue("@name", "Ada")]);
var count = await Harness.QueryScalarAsync<int>( "SELECT COUNT(*) FROM customers WHERE email = @email", [new DbParameterValue("@email", "ada@example.test")]);
Assert.AreEqual(1, count); }}One small class per engine says which engine to use and where its schema lives:
[TestClass]public sealed class MsSqlCustomerTests : CustomerTests{ protected override DbEngine CreateEngine() => new MsSqlEngine(); protected override string SchemaRoot => "Schema/mssql";}[TestClass]public sealed class PostgresCustomerTests : CustomerTests{ protected override DbEngine CreateEngine() => new PostgreSqlEngine(); protected override string SchemaRoot => "Schema/postgres";}[TestClass]public sealed class MySqlCustomerTests : CustomerTests{ protected override DbEngine CreateEngine() => new MySqlEngine(); protected override string SchemaRoot => "Schema/mysql";}The getting started guide builds this up step by step.
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 on first use and shared by the run. |
| How does the schema get there? | Schema files on disk, applied in order, with each engine’s own splitting rules. |
| A test dropped a column and the next test failed on it | The shape guard fails the test that did it, naming the object and the change. |
| One test’s rows leaked into the next | CleanAsync() empties every table whatever the foreign keys, and resets identity counters. |
| “The suite is slow” | Per-test timings in a TSV file (DBTEST_TIMING=1). |
| The same assertions written three times | The engine sits behind one abstract class. |
Packages
Section titled “Packages”| Package | Purpose |
|---|---|
DbTest.Core |
The engine-agnostic harness, without any test framework. |
DbTest.MSTest |
DbTestBase and DbTestRun for MSTest. Brings DbTest.Core. |
DbTest.MsSql |
The SQL Server engine. |
DbTest.PostgreSql |
The PostgreSQL engine. |
DbTest.MySql |
The MySQL engine. |
DbTest.MsSql.Dacpac |
Deploys a SQL Server .dacpac as the schema. |
DbTest.Cli |
The dbtest command-line tool. |
Where to go next
Section titled “Where to go next”- Getting started: install, requirements and a first test.
- Engines: what each engine runs and how to configure its container.
- The schema: how SQL files are discovered, ordered and applied.
- SQL Server database projects: test a
.sqlprojthrough its.dacpac. - Extending db-test: write your own engine or schema source.