一键导入
integration
Integration tests — real database, build tags, transaction isolation.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Integration tests — real database, build tags, transaction isolation.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Visual-design discipline for forge frontends — brief the work before building (never invent an aesthetic without user input), declare the design system before coding, lean on the component library, use restrained color/type systems, never hand-draw complex SVGs, and verify visually before declaring done.
Write Next.js frontends — generated hooks, component library, Tailwind v4, visual verification, and Connect RPC clients.
Outbound boundary translators. One adapter per third-party system / queue / storage backend; narrow interface, vendor-neutral callers.
Write Connect RPC handlers — proto-driven codegen, the thin-translation handler pattern (validate, extract auth, convert proto↔internal, call service, wrap errors via `svcerr.Wrap`), middleware, and testing. Business logic lives in `internal/handlers/<svc>/contract.go`, never in handlers.
Forge project conventions and architecture — project structure, generated vs hand-written code, the generate pipeline, proto annotations, contracts, wiring, and naming.
Use-case orchestrators that compose two or more adapters/services. Deps are interfaces only — designed for unit tests with all-mock collaborators.
| name | integration |
| description | Integration tests — real database, build tags, transaction isolation. |
Integration tests run against a real database and verify that queries, migrations, and data access layers work correctly. They are invisible to plain go test because they require a build tag.
Every integration test file must include the build constraint at the top:
//go:build integration
package handlers_test
This keeps them out of go test ./... and ensures they only run when explicitly requested.
testing.Short()Forge integration tests use the //go:build integration build tag — not testing.Short() with t.Skip(...). The two mechanisms answer different questions:
| Mechanism | What it does | Use when |
|---|---|---|
//go:build integration | The Go toolchain physically excludes the file from go build / go test unless -tags integration is passed. No compile, no link, no skip-at-runtime. | The test needs infra that may not be present (DB, broker, network). The default test run must not even attempt it. |
if testing.Short() { t.Skip(...) } | The test is always compiled and runs by default. go test -short skips it at runtime. | The test is hermetic but optionally slow — e.g. a fast unit test that has an extra long-running scenario behind -short. |
Rule for forge projects: all DB-backed / RPC-roundtrip / infra-dependent tests use the build tag. Reserve testing.Short() for the rare case of a hermetic test with a fast/slow toggle. If your test would t.Skip because it can't reach a database, it belongs behind //go:build integration instead.
The matching convention for end-to-end tests is //go:build e2e.
forge test integration # runs all integration tests
forge test integration --service <name> # one service only
forge test --coverage # includes integration in coverage
The forge test integration command automatically adds -tags integration to the Go test invocation.
Integration tests require a running Postgres instance via docker-compose. If you're already running the stack, you're set:
forge up --env=dev # starts the full stack including postgres
pkg/testkit ships the harness pieces an integration test needs — a migrated test DB, authed contexts, claims options — and pkg/tdd exposes table-driven entry points on top (tdd.SetupMockDB, app.NewMigratedTestDB). Reach for those before hand-rolling connection + migration boilerplate. See the testing and testing/patterns skills for the cheat sheet.
Mock discipline at this tier: the subject and its real internal collaborators (including the DB) stay real — that's the whole point of integration. Mock only third-party boundaries you don't own.
Each test runs inside a database transaction that is rolled back in cleanup. This guarantees complete state isolation between tests:
func TestCreateUser(t *testing.T) {
tx := testdb.Begin(t) // starts transaction
t.Cleanup(func() { tx.Rollback() })
queries := db.New(tx)
// ... test with real queries
}
Integration tests are the right place to verify sqlc-generated queries against the real schema. Test the actual SQL — don't mock the database when the query behavior is what you're validating.
t.Cleanup() (transaction rollback handles this automatically)