🤖 dbtest
amadan.net/rastrillo/rastrillo/dbtest
A fresh, migrated SQLite database for every test, without migrating it every time. The first test that asks builds one migrated template, and every test gets its own copy of that file.
A copy is much cheaper than a migration. Migrating means SQLite parsing and running every statement in your schema, and every test pays it again. In Tito Go a fresh database took 207ms and a copy took 2.5ms.
Template
type Template struct { /* unexported */ }
func FromSet(s *migrate.Set) *Template
func New(build func(path string) error) *Template
func (tp *Template) Remove()
Declare one template per test package, beside the tests that use it.
FromSet builds it with migrate.Apply, so a migration you add is in
the template the next time the tests run.
var schema = dbtest.FromSet(migrate.Merge(sessions.Schema, app.Schema))
func TestMain(m *testing.M) {
code := m.Run()
schema.Remove()
os.Exit(code)
}
Use New when your migrations are not a migrate.Set. Your build
function must leave a migrated database at path and close every
connection to it before it returns. If a connection is still open, the
template is refused: its newest pages are still in the -wal file, and
a copy would miss them.
Call Remove from TestMain after m.Run. The template outlives every
test, so no test can clean it up.
Getting a copy
func (tp *Template) Open(t testing.TB) *sql.DB
func (tp *Template) Path(t testing.TB) string
func (tp *Template) CopyTo(t testing.TB, path string)
Open copies the template and opens the copy the way db.Open opens
its writer: foreign keys on, one connection, so migrate.Pool accepts
it. It is closed when the test ends.
If your app opens its database itself, use Path: it puts the copy in
the test's temporary directory and returns its path, as in
db.Open(schema.Path(t), nil). CopyTo puts the copy at a path you
choose.
If the template cannot be built, the test that asked fails with the build's error. Every later test fails the same way; the build is not tried again.
What stays out of the template
Keep the template to schema. Create test data in the test itself: a row made when the template was built carries that time, not the time the test ran.
If a migration creates a value that must be different in every database, such as a random key, every copy gets the template's. Set a new one after you copy.
Read this page as markdown — exact, unstyled, and cheap for an agent to fetch.