Examples

Here are some practical examples of how to use embedded-postgres in common testing scenarios.

Example 1: Testing Database Migrations with Goose

You can use embedded-postgres to create a real database to test your schema migrations.

import (
    "testing"

    embeddedpostgres "github.com/fergusstrange/embedded-postgres"
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"
    "github.com/pressly/goose/v3"
)

func Test_GooseMigrations(t *testing.T) {
    // Start the database
    database := embeddedpostgres.NewDatabase()
    if err := database.Start(); err != nil {
        t.Fatal(err)
    }
    defer database.Stop()

    // Connect to the database
    db, err := sqlx.Connect("postgres", "host=localhost port=5432 user=postgres password=postgres dbname=postgres sslmode=disable")
    if err != nil {
        t.Fatal(err)
    }

    // Run migrations
    if err := goose.Up(db.DB, "./migrations"); err != nil {
        t.Fatal(err)
    }

    // You can now verify that the schema was created correctly
    // by querying the tables, columns, etc.
}

Example 2: Testing an HTTP Handler

This example shows how to test a web application's HTTP handler that depends on a database.

import (
    "net/http"
    "net/http/httptest"
    "testing"

    embeddedpostgres "github.com/fergusstrange/embedded-postgres"
    // Assume NewApp() sets up your application and routes
)

func Test_SimpleHttpWebApp(t *testing.T) {
    // Start the database for the test
    database := embeddedpostgres.NewDatabase()
    if err := database.Start(); err != nil {
        t.Fatal(err)
    }
    defer database.Stop()

    // Your application setup function (NewApp) would internally
    // connect to the database we just started.
    app := NewApp() // This function needs to be adapted to connect to the test DB.

    request := httptest.NewRequest("GET", "/beer-catalogue?name=Punk%20IPA", nil)
    recorder := httptest.NewRecorder()

    app.router.ServeHTTP(recorder, request)

    if recorder.Code != http.StatusOK {
        t.Fatalf("expected 200 but receieved %d", recorder.Code)
    }

    expectedPayload := `[{"id":1,"name":"Punk IPA","consumed":true,"rating":68.29}]`
    actualPayload := recorder.Body.String()

    if actualPayload != expectedPayload {
        t.Fatalf("expected %s but receieved %s", expectedPayload, actualPayload)
    }
}

Example 3: Running Multiple Test Cases Against a Single Database

For faster tests, you can start the database once and run multiple sub-tests against it. You'll need to be careful to clean up or reset state between tests.

import (
    "fmt"
    "reflect"
    "testing"

    embeddedpostgres "github.com/fergusstrange/embedded-postgres"
    "github.com/jmoiron/sqlx"
)

func Test_ManyTestsAgainstOneDatabase(t *testing.T) {
    database := embeddedpostgres.NewDatabase()
    if err := database.Start(); err != nil {
        t.Fatal(err)
    }
    defer database.Stop()

    db, err := sqlx.Connect("postgres", "host=localhost port=5432 user=postgres password=postgres dbname=postgres sslmode=disable")
    if err != nil {
        t.Fatal(err)
    }

    // Optional: apply migrations once before all tests
    // goose.Up(db.DB, "./migrations")

    tests := []struct {
        name string
        fn   func(t *testing.T, db *sqlx.DB)
    }{
        {
            name: "Test query for existing item",
            fn: func(t *testing.T, db *sqlx.DB) {
                // test logic here...
            },
        },
        {
            name: "Test inserting a new item",
            fn: func(t *testing.T, db *sqlx.DB) {
                // Ensure cleanup for this specific test case
                defer db.Exec("DELETE FROM my_table WHERE name = 'Test Item'")
                // test logic here...
            },
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            tt.fn(t, db)
        })
    }
}