Skip to content

Testing Generated Code

kiln doesn't generate tests - testing strategies are too project-specific. But the generated code is designed to be easy to test.

Testing Handlers

Handlers depend on a store interface, not a concrete type. Mock the interface:

package handlers_test

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

    "yourmodule/generated/handlers"
    "yourmodule/generated/models"
    "github.com/gofrs/uuid/v5"
)

type mockUserStore struct{}

func (m *mockUserStore) Get(ctx context.Context, id uuid.UUID) (*models.User, error) {
    return &models.User{ID: id, Name: "Alice", Email: "[email protected]"}, nil
}

// ... implement other interface methods

func TestGetUser(t *testing.T) {
    handler := handlers.NewUserHandler(&mockUserStore{})

    req := httptest.NewRequest("GET", "/api/v1/users/"+uuid.Must(uuid.NewV4()).String(), nil)
    w := httptest.NewRecorder()

    handler.Get(w, req)

    if w.Code != http.StatusOK {
        t.Errorf("got status %d, want 200", w.Code)
    }
}

Testing Store Methods

Store constructors accept bob.Executor, which both bob.DB and bob.Tx satisfy. This means you can pass a transaction directly for test isolation. Use a real test database:

package store_test

import (
    "context"
    "database/sql"
    "testing"

    "yourmodule/generated/models"
    "yourmodule/generated/store"
    "github.com/stephenafamo/bob"
    _ "github.com/jackc/pgx/v5/stdlib"
)

func testDB(t *testing.T) bob.DB {
    t.Helper()
    sqlDB, err := sql.Open("pgx", "postgres://test:test@localhost:5432/test?sslmode=disable")
    if err != nil {
        t.Fatalf("opening test DB: %v", err)
    }
    t.Cleanup(func() { sqlDB.Close() })
    return bob.NewDB(sqlDB)
}

func TestCreateUser(t *testing.T) {
    db := testDB(t)
    s := store.NewUserStore(db)

    user, err := s.Create(context.Background(), models.CreateUserRequest{
        Email: "[email protected]",
        Name:  "Test User",
    })
    if err != nil {
        t.Fatalf("Create: %v", err)
    }
    if user.Email != "[email protected]" {
        t.Errorf("email = %q, want [email protected]", user.Email)
    }
}

Test Isolation

For test isolation, use txdb to wrap each test in a transaction that rolls back:

import "github.com/DATA-DOG/go-txdb"

func init() {
    txdb.Register("txdb", "pgx", "postgres://test:test@localhost:5432/test?sslmode=disable")
}

func testDB(t *testing.T) bob.DB {
    sqlDB, _ := sql.Open("txdb", t.Name())
    t.Cleanup(func() { sqlDB.Close() })
    return bob.NewDB(sqlDB)
}

Integration Tests

For full HTTP integration tests, wire up the real server:

func TestAPI(t *testing.T) {
    db := testDB(t)
    mux := http.NewServeMux()

    userStore := store.NewUserStore(db)
    userHandler := handlers.NewUserHandler(userStore)
    generated.RegisterRoutes(mux, userHandler)

    server := httptest.NewServer(mux)
    defer server.Close()

    resp, err := http.Get(server.URL + "/api/v1/users")
    if err != nil {
        t.Fatalf("GET /users: %v", err)
    }
    if resp.StatusCode != 200 {
        t.Errorf("status = %d, want 200", resp.StatusCode)
    }
}