Skip to content

Testing

Because goboot generates plain Go and every layer depends inward through an interface, you test the way you always have — no framework test harness.

Service layer (no database)

Depend on the repository interface, and a hand-written in-memory fake tests your business rules with zero infrastructure:

type fakeRepo struct{ items map[string]domain.Todo }

func (f *fakeRepo) FindByID(_ context.Context, id string) (*domain.Todo, error) {
    t, ok := f.items[id]
    if !ok {
        return nil, db.ErrNoRows
    }
    return &t, nil
}
// ...implement the rest of the interface...

func TestCreate(t *testing.T) {
    svc := service.NewTodoService(&fakeRepo{items: map[string]domain.Todo{}})
    todo, err := svc.Create(context.Background(), "buy milk")
    // assert on todo/err
}

HTTP layer

Register the generated routes on an httptest server and drive real requests:

components, _ := generated.BuildComponents(fakeDB)
mux := http.NewServeMux()
generated.RegisterRoutes(mux, components, runtime.DefaultHTTPHandlerDependencies())
srv := httptest.NewServer(mux)
defer srv.Close()

resp, _ := http.Get(srv.URL + "/todos/42")

Repository layer

Repositories depend only on the runtime/db interfaces, so a fake db.DBTX returning canned rows tests scanning and argument binding without a database — or run an integration test against a real DB via an adapter, gated on an env var:

func TestPostgres(t *testing.T) {
    dsn := os.Getenv("TEST_DSN")
    if dsn == "" {
        t.Skip("set TEST_DSN to run")
    }
    // pgxpool.New(...) → pgxadapter.NewProvider(...) → drive the repo
}

Generated wiring

Commit the generated files and let go build ./... and CI compile them — a compile failure means the wiring drifted from the annotated code. Regenerate with goboot generate ./... (it self-heals stale output before loading).