Documentation
¶
Overview ¶
Package dbcleaner puts a database back to its baseline after a test, the way Ruby's DatabaseCleaner does: pick a strategy, start a session, clean it.
Three strategies are built in:
- Transaction runs the whole test inside one transaction and rolls it back. The fastest, but the test must use the *sql.DB it is given: another connection (a server in another process, a second pool) cannot see its rows.
- Truncation commits normally and runs TRUNCATE on the tables afterwards.
- Deletion commits normally and runs DELETE on the tables afterwards, children before parents.
Null cleans nothing. The package knows nothing about an ORM: it hands out a *database/sql.DB and runs one setup callback (migrations, fixtures) per process.
What differs between engines is a Dialect; the one for a URL is picked by its scheme among those the program imported (import _ "github.com/mokevnin/dbcleaner/postgres").
Index ¶
- func DefaultGuard(database string) bool
- func Register(scheme string, d Dialect)
- type Cleaner
- func (c *Cleaner) CleanWith(ctx context.Context, s Strategy) error
- func (c *Cleaner) Cleaning(ctx context.Context, fn func(db *sql.DB) error) error
- func (c *Cleaner) Close() error
- func (c *Cleaner) DB(t testing.TB) *sql.DB
- func (c *Cleaner) Start(ctx context.Context) (*Session, error)
- func (c *Cleaner) URL(ctx context.Context) (string, error)
- type Deleter
- type Dialect
- type Leaser
- type Option
- func WithDialect(d Dialect) Option
- func WithDriver(name string) Option
- func WithGuard(allow func(database string) bool) Option
- func WithSetup(fn func(ctx context.Context, db *sql.DB) error) Option
- func WithStrategy(s Strategy) Option
- func WithTemplate(version string, build func(ctx context.Context, db *sql.DB, url string) error) Option
- type Session
- type Strategy
- type TableOption
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefaultGuard ¶
DefaultGuard allows a database whose name contains "test", "e2e" or "scratch". Wiping a database is the one irreversible thing this package does, so a URL that still points at a development database is refused rather than emptied.
Types ¶
type Cleaner ¶
type Cleaner struct {
// contains filtered or unexported fields
}
Cleaner starts cleaning sessions against one database. Build it once per test process (a package variable next to TestMain) and share it between tests.
func New ¶
New returns a Cleaner for the database at url. Nothing connects until the first session starts.
func (*Cleaner) CleanWith ¶
CleanWith cleans once with the given strategy, whatever the Cleaner's own is. The usual use is a Truncation before the suite, then Transaction per test.
func (*Cleaner) Cleaning ¶
Cleaning runs fn in a session and cleans it afterwards, even when fn fails.
func (*Cleaner) Close ¶
Close releases the shared pool the committing strategies use and the worker database lease. A Cleaner that only ran Transaction on its own database holds nothing to close.
func (*Cleaner) DB ¶
DB starts a session for the test and cleans it when the test ends. A failure to start or to clean fails the test.
type Deleter ¶
Deleter is a Dialect that deletes the rows of a table itself, for an engine where a plain DELETE FROM does not do. Deletion calls it, inside its transaction, for every table.
type Dialect ¶
type Dialect interface {
// Driver is the database/sql driver name the URL is opened with by default. The
// package of the dialect imports the driver.
Driver() string
// Quote quotes one identifier.
Quote(identifier string) string
// CurrentDatabase is the name of the database db is connected to, for the guard.
CurrentDatabase(ctx context.Context, db *sql.DB) (string, error)
// Tables lists the ordinary tables of the current schema, sorted, leaving out those
// that cleaning another table cleans too (partitions).
Tables(ctx context.Context, db *sql.DB) ([]string, error)
// References maps a table to the tables it holds a foreign key to. A table that
// references itself is not listed as referencing itself.
References(ctx context.Context, db *sql.DB) (map[string][]string, error)
// Truncate empties the tables, resets what counts rows (identity columns, sequences)
// and does not stop at a foreign key between them or to them.
Truncate(ctx context.Context, db *sql.DB, tables []string) error
}
Dialect is what the cleaner has to ask one database engine: how to quote a name, which tables there are, which of them reference which, and how to empty them. Everything else is database/sql. The engines this module knows live in subpackages (dbcleaner/postgres), which register themselves for their URL schemes when imported.
type Leaser ¶
type Leaser interface {
Dialect
// Lease prepares the databases next to the one in rawURL as [WithTemplate] describes and
// returns the URL of this process's worker. release gives the lease back; it is called
// by [Cleaner.Close].
Lease(ctx context.Context, rawURL, driver, version string, build func(ctx context.Context, db *sql.DB, url string) error) (leased string, release func() error, err error)
}
Leaser is a Dialect that can give a process a database of its own, cloned from a template: what WithTemplate needs. An engine with no cheap way to clone a database leaves it out.
type Option ¶
type Option func(*Cleaner)
Option tunes a Cleaner.
func WithDialect ¶
WithDialect sets the dialect, for a URL whose scheme names none (a key=value DSN) or to use another than the registered one.
func WithDriver ¶
WithDriver names the database/sql driver the URL is opened with. The default is the dialect's own; any other driver must be imported by the caller.
func WithGuard ¶
WithGuard replaces the check that the database may be wiped, run before Truncation and Deletion touch it (see DefaultGuard). A nil guard allows every database.
func WithSetup ¶
WithSetup registers the callback that brings the database to its baseline: migrate the schema, load the fixtures. It runs once per Cleaner, on a committing connection, before the first session starts. Its error fails every session.
func WithStrategy ¶
WithStrategy sets the strategy Cleaner.Start and Cleaner.DB use. The default is Transaction.
func WithTemplate ¶
func WithTemplate(version string, build func(ctx context.Context, db *sql.DB, url string) error) Option
WithTemplate makes the Cleaner work on a database of its own instead of the one in its URL, so that test processes (one per package under `go test ./...`) run in parallel without sharing rows or locks.
The dialect must be a Leaser (PostgreSQL's is). What follows is what it does, in PostgreSQL's terms. Two kinds of database are kept next to the one in the URL, called base:
- base_tpl_<hash of version>, the template (a base too long for a 63-byte name is shortened, with a hash of it). It is built once per version, by whichever process comes first and under an advisory lock, by running build on an empty database; others wait for it. A template that exists is always complete. Older versions are dropped.
- base_w<i>, the workers. A process leases the lowest free index with a session advisory lock, which the server releases when the process ends, however it ends: there is no cleanup hook to forget, and the number of databases stays bounded by the number of concurrent processes. A worker is cloned from the template when it is missing or was cloned from another version, and is otherwise reused as it is.
version names everything that decides what build produces: bump it when the schema changes (a hash of the migration files, say). build gets the template database and its URL, for a tool that opens its own connection.
What differs per process belongs in WithSetup, which runs on the worker every time (fixtures with relative dates, for one): it also wipes whatever an earlier process left. The database to hand to code that opens its own connection is Cleaner.URL.
The role needs the right to create databases. A Cleaner whose first session starts inside a testing/synctest bubble must be closed inside it, or never: a test process just ends.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is one test's view of the database, from Cleaner.Start to Session.Clean.
type Strategy ¶
type Strategy interface {
// contains filtered or unexported methods
}
Strategy is how a session undoes a test's changes.
func Deletion ¶
func Deletion(opts ...TableOption) Strategy
Deletion empties the tables with DELETE after the test, children before parents. It is the cheaper choice when a test touches few of many tables, and the one that works without the TRUNCATE privilege. Sequences are not reset. A foreign-key cycle among the cleaned tables is an error: use Truncation there.
Migration bookkeeping tables are never cleaned (see Except).
func Null ¶
func Null() Strategy
Null cleans nothing: the session is the committing pool, untouched afterwards.
func Transaction ¶
func Transaction() Strategy
Transaction rolls the test back. Every session owns one connection with one open transaction; the code under test sees it as an ordinary *sql.DB, and its own Begin/Commit/Rollback become savepoints, so nested transactions work and still vanish with the session. The connection is limited to one: a second would start a second transaction that does not see the first one's writes.
func Truncation ¶
func Truncation(opts ...TableOption) Strategy
Truncation empties the tables with TRUNCATE after the test (the dialect decides how: on PostgreSQL, RESTART IDENTITY CASCADE). Rows are committed, so any connection sees them (a server under test in another process, a second pool), at the price of a slower clean and sessions that must not run in parallel against one database.
Migration bookkeeping tables are never truncated (see Except).
type TableOption ¶
type TableOption func(*filter)
TableOption narrows the tables Truncation and Deletion clean.
func Except ¶
func Except(tables ...string) TableOption
Except leaves these tables alone, on top of the migration tables.
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
conformance
Package conformance is the test suite every dialect has to pass: the same behaviour of the strategies and the guard, on whatever engine the dialect is for.
|
Package conformance is the test suite every dialect has to pass: the same behaviour of the strategies and the guard, on whatever engine the dialect is for. |
|
Package mysql is the MySQL (and MariaDB) dialect of dbcleaner, on the go-sql-driver/mysql driver.
|
Package mysql is the MySQL (and MariaDB) dialect of dbcleaner, on the go-sql-driver/mysql driver. |
|
Package postgres is the PostgreSQL dialect of dbcleaner.
|
Package postgres is the PostgreSQL dialect of dbcleaner. |
|
Package sqlite is the SQLite dialect of dbcleaner, on the pure-Go modernc.org/sqlite driver.
|
Package sqlite is the SQLite dialect of dbcleaner, on the pure-Go modernc.org/sqlite driver. |