Skip to content

Testing

Picking a Registration Path

Den exposes three ways to register document types. Use them roughly like this:

Where you are Reach for Why
App code, types known at startup den.WithTypes(&T{}, ...) as an Open option Single expression sets up the whole DB. Idempotent — safe on every startup.
App code, types discovered after Open den.Register(ctx, db, &T{}, ...) Same registration logic, called once you know the types.
Test code dentest.MustOpen(t, &T{}, ...) (or MustOpenPostgres(...)) Wraps OpenURL + Register + t.Cleanup in one call.

The dentest helpers also import both backend packages for their side effects, so test files do not need the _ "github.com/oliverandrich/den/backend/sqlite" blank import that production code uses.

SQLite Test Helper

The dentest package provides a one-liner to create a file-backed SQLite database in a temporary directory, pre-register document types, and auto-close when the test ends:

import (
    "context"
    "testing"

    "github.com/oliverandrich/den"
    "github.com/oliverandrich/den/dentest"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestProductInsert(t *testing.T) {
    db := dentest.MustOpen(t, &Product{}, &Category{})

    ctx := context.Background()
    p := &Product{Name: "Widget", Price: 9.99}
    err := den.Save(ctx, db, p)
    require.NoError(t, err)
    assert.NotEmpty(t, p.ID)

    found, err := den.FindByID[Product](ctx, db, p.ID)
    require.NoError(t, err)
    assert.Equal(t, "Widget", found.Name)
    assert.Equal(t, 9.99, found.Price)
}

dentest.MustOpen creates a real SQLite database file inside t.TempDir() and registers t.Cleanup to close it automatically. No manual teardown needed.

PostgreSQL Test Helper

For testing against PostgreSQL, use dentest.MustOpenPostgres:

func TestProductInsertPG(t *testing.T) {
    connStr := "postgres://user:pass@localhost/testdb"
    db := dentest.MustOpenPostgres(t, connStr, &Product{}, &Category{})

    ctx := context.Background()
    p := &Product{Name: "Widget", Price: 9.99}
    err := den.Save(ctx, db, p)
    require.NoError(t, err)

    found, err := den.FindByID[Product](ctx, db, p.ID)
    require.NoError(t, err)
    assert.Equal(t, "Widget", found.Name)
}

Cleanup behavior

MustOpenPostgres does not create a fresh schema — it connects to the database you supply. On t.Cleanup, it drops every collection that was registered through the helper (by name) and then closes the connection. Data in unrelated collections is untouched.

Picking a connection string

Use dentest.PostgresURL() to pull the DSN from the DEN_POSTGRES_URL environment variable (default postgres://localhost/den_test). This keeps tests portable between developer machines and CI.

Parallel tests and collection names

Tests that run in parallel against the same database must register disjoint document types, or each test must use its own database. Two tests registering Product against the same database will race on the shared collection; the helper does not sandbox them.

Complete Test Example

A test that inserts, queries, updates, and deletes:

func TestProductLifecycle(t *testing.T) {
    db := dentest.MustOpen(t, &Product{})
    ctx := context.Background()

    // Insert
    p := &Product{Name: "Gadget", Price: 19.99}
    require.NoError(t, den.Save(ctx, db, p))
    assert.NotEmpty(t, p.ID)

    // Query
    products, err := den.NewQuery[Product](db,
        where.Field("price").Gt(10.0),
    ).All(ctx)
    require.NoError(t, err)
    assert.Len(t, products, 1)
    assert.Equal(t, "Gadget", products[0].Name)

    // Update
    p.Price = 24.99
    require.NoError(t, den.Save(ctx, db, p))

    refreshed, err := den.FindByID[Product](ctx, db, p.ID)
    require.NoError(t, err)
    assert.Equal(t, 24.99, refreshed.Price)

    // Delete
    require.NoError(t, den.Delete(ctx, db, p))

    _, err = den.FindByID[Product](ctx, db, p.ID)
    assert.ErrorIs(t, err, den.ErrNotFound)
}

Tip

Use testify for assertions. require aborts the test on failure (use for setup steps), assert records the failure and continues (use for verification steps).