TestDock is a Go library that simplifies database testing by providing an easy way to create and manage test databases in realistic scenarios, instead of using mocks. It supports running tests against both Docker containers and external databases, with built-in support for MongoDB and various SQL databases.
-
Multiple Database Support
- MongoDB:
GetMongoDatabasefunction - PostgreSQL:
GetPgxPool,GetPqConn, and reusable migrated templates forpgxtests - MySQL:
GetMySQLConnfunction - Any other SQL database supported by
database/sqlhttps://go.dev/wiki/SQLDrivers:GetSQLConnfunction
- MongoDB:
-
Flexible Test Environment
- Docker container support for isolated testing
- External database support for CI/CD environments
- Auto-mode that switches based on environment variables
-
Database Migration Support
- Integration with goose
- Integration with golang-migrate
- User provided migration tool
- Automatic migration application during test setup
-
Robust Connection Handling
- Automatic retry mechanisms
- Automatic selection of a free host port when deploying containers
- Graceful cleanup after tests
go get github.com/n-r-w/testdock/v2@latestGetPgxPool: PostgreSQL connection pool (pgx driver)NewPostgresTemplate: Parent-owned migrated PostgreSQL source for fast physical clonesGetPqConn: PostgreSQL connection (libpq driver)GetMySQLConn: MySQL connectionGetSQLConn: Generic SQL database connectionGetMongoDatabase: MongoDB database
The connection string format is driver-specific. For example:
- For PostgreSQL:
postgres://user:password@localhost:5432/database?sslmode=disable - For MySQL:
root:password@tcp(localhost:3306)/database?parseTime=true - For MongoDB:
mongodb://user:password@localhost:27017/database
Depending on the chosen mode (WithMode), the connection string is used differently:
- The connection string is used directly to connect to the database
- The connection string is used to generate the Docker container configuration
- The port value is used 1) as the port inside the container, 2) as the external access port to the database
- If this port is already taken on the host, then TestDock tries to find a free port by incrementing its value by 1 until a free port is found
- If the environment variable
TESTDOCK_DSN_<DRIVER_NAME>is not set, then TestDock chooses theRunModeDockermode and uses the input string as the container configuration - If the environment variable
TESTDOCK_DSN_<DRIVER_NAME>is set, then TestDock chooses theRunModeExternalmode and uses the string from the environment variable to connect to the external database. In this case, thedsnparameter of the constructor function is ignored. Thus, in this mode, thedsnparameter is used as a fallback if the environment variable is not set.
Each Get... call creates an independent temporary database, so the call can be made inside a test that uses t.Parallel(). Keep the returned resource in that test; do not share it with other tests. TestDock registers cleanup through testing.TB.Cleanup.
In Docker mode, calls with the same resolved DSN reuse one container. TestDock runs at most four database lifecycle operations concurrently per test process and DSN. Creation, automatic migrations, and cleanup share this limit, so waiting operations do not consume database connections. Separate go test package processes do not share the limiter. PostgreSQL cleanup retries DROP DATABASE after SQLSTATE 53300 using WithRetryTimeout and WithTotalRetryDuration; an exhausted cleanup error is logged without changing the test result.
When a parent test has many PostgreSQL children with identical migrations and initial data, use NewPostgresTemplate. The parent prepares one source database, and each child receives an isolated physical clone through PostgresTemplate.GetPgxPool. The source remains alive until the parent and all its subtests complete.
import (
"testing"
"github.com/n-r-w/testdock/v2"
)
func TestDatabase(t *testing.T) {
t.Parallel()
pool, _ := testdock.GetPgxPool(t,
testdock.DefaultPostgresDSN,
testdock.WithMigrations("migrations", testdock.GooseMigrateFactoryPGX),
)
// Prepare isolated data, run the code under test, and assert through pool.
// The pool and temporary database are cleaned up automatically.
}import (
"testing"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/n-r-w/testdock/v2"
)
func TestDatabaseGroup(t *testing.T) {
template := testdock.NewPostgresTemplate(
t,
testdock.DefaultPostgresDSN,
testdock.WithPostgresTemplateOptions(
testdock.WithMigrations("migrations", testdock.GooseMigrateFactoryPGX),
),
testdock.WithPostgresTemplateSetup(func(
tb testing.TB,
pool *pgxpool.Pool,
_ testdock.Informer,
) {
// Add shared seed data once. The setup pool is closed before cloning starts.
}),
)
for _, name := range []string{"first", "second"} {
t.Run(name, func(t *testing.T) {
t.Parallel()
pool, _ := template.GetPgxPool(t)
// Mutations are isolated from every other clone.
})
}
}WithPostgresTemplateOptions applies existing database options only to the source. Automatic migrations and WithPostgresTemplateSetup run once. PostgreSQL requires the source database to have no open connections while it is copied, so TestDock closes the setup pool before returning the template.
CREATE DATABASE ... TEMPLATE copies database objects and data, but PostgreSQL does not copy database-level GRANT permissions or settings created through ALTER DATABASE.
import (
"testing"
"github.com/n-r-w/testdock/v2"
)
func TestMongoDB(t *testing.T) {
// Get a connection to a test database
db, _ := testdock.GetMongoDatabase(t, testdock.DefaultMongoDSN,
testdock.WithMode(testdock.RunModeDocker),
testdock.WithMigrations("migrations", testdock.GolangMigrateFactory),
)
// Use the database for your tests
// The database will be automatically cleaned up after the test
}TESTDOCK_DSN_PGX,TESTDOCK_DSN_POSTGRES- PostgreSQL-specific connection stringsTESTDOCK_DSN_MYSQL- MySQL-specific connection stringTESTDOCK_DSN_MONGODB- MongoDB-specific connection stringTESTDOCK_DSN_<DRIVER_NAME>- Custom connection string for a specific driver
WithRetryTimeout(duration): Configure connection retry timeout (default 3s). Must be less than totalRetryDurationWithTotalRetryDuration(duration): Configure total retry duration (default 30s). Must be greater than retryTimeoutWithCloseTimeout(duration): Configure cleanup timeout for closing returned resources (default 30s). Must be greater than 0. It coverspgxpool.Pool.Close,sql.DB.Close, andmongo.Client.Disconnect. It does not cover SQLDROP DATABASE, MongoDBDrop, or Docker cleanup.
WithRetryTimeout and WithTotalRetryDuration also control retries for PostgreSQL SQLSTATE 53300 while a migration connection is being established. Migration execution itself is never retried.
WithDockerSocketEndpoint(endpoint): Custom Docker daemon socketWithDockerPort(port): Override container port mappingWithUnsetProxyEnv(bool): Unset proxy environment variables
If close timeout is reached, the test fails and later cleanup functions continue. A timeout usually means the test leaked a connection: Rows was not closed, QueryRow was used without Scan, or a transaction was not finished.
WithConnectDatabase(name): Override connection databaseWithPrepareCleanUp(func): Custom cleanup handlers. The default is empty, butGetPgxPoolandGetPqConnfunctions use it to automatically apply cleanup handlers to disconnect all users from the database before cleaning up.WithLogger(logger): Custom logging implementation
DefaultPostgresDSN: Default PostgreSQL connection stringDefaultMySQLDSN: Default MySQL connection stringDefaultMongoDSN: Default MongoDB connection string
TestDock supports two popular migration tools:
https://github.com/pressly/goose
Parallel tests must not use Goose package-level state APIs such as goose.SetDialect, goose.SetBaseFS, goose.Up*, or goose.Down*. Use WithMigrations, WithMigrationsToVersion, ApplyMigrations, or ApplyMigrationsToVersion. When a rollback is required, create a separate goose.Provider for each temporary database. Close it before the migration helper returns instead of using testing.TB.Cleanup, and preserve migration and close errors with errors.Join.
db, _ := GetPqConn(t,
"postgres://postgres:secret@127.0.0.1:5432/postgres?sslmode=disable",
testdock.WithMigrations("migrations/pg/goose", testdock.GooseMigrateFactoryPQ),
testdock.WithDockerImage("17.2"),
)https://github.com/golang-migrate/migrate
db, _ := GetMongoDatabase(t,
testdock.DefaultMongoDSN,
testdock.WithDockerRepository("mongo"),
testdock.WithDockerImage("6.0.20"),
testdock.WithMigrations("migrations/mongodb", testdock.GolangMigrateFactory),
)You can also use a custom migration tool implementing the testdock.MigrateFactory interface.
- Go 1.26 or higher
- Docker (when using
RunModeDockerorRunModeAuto)
MIT License - see LICENSE for details