Skip to content
Back to skills

Configure Backend Tests

ASecurity

Use when a backend service needs a test setup or its tests mock the database. Builds the pyramid - pure unit tests, real-Postgres integration via testcontainers, HTTP-level tests, OpenAPI contract tests, factories and CI-ready layout (Vitest, go test, pytest).

  • 5 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 10, 2026
developmentpythongobashsqlnextjsnodefastapidockergitapi

Works with

  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 3 files and shows the line behind each finding

Scanned October 10, 2026

npx -y skills add VelimirMueller/lab-claude-skills --skill configure-backend-tests --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Configure Backend Tests?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Configure Backend Tests
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/velimirmueller-configure-backend-tests/badge)](https://www.skillsdirectory.com/skills/velimirmueller-configure-backend-tests)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: configure-backend-tests
description: Use when a backend service needs a test setup or its tests mock the database. Builds the pyramid - pure unit tests, real-Postgres integration via testcontainers, HTTP-level tests, OpenAPI contract tests, factories and CI-ready layout (Vitest, go test, pytest).
---

# Configure Backend Tests

Rules and rationale: [test-patterns.md](test-patterns.md). Code per track: [test-tracks.md](test-tracks.md). Layout follows the tests rows of [service-layout.md](../_shared/service-layout.md). The principle behind it all: "Tests at the boundary that pays" in [engineering-principles.md](../../core/_shared/engineering-principles.md).

## 1. Audit (change nothing)

```bash
cat .claude/stack-profile.md ~/.claude/stack-profile.md 2>/dev/null   # backend.track, tests.layout (tests-dir|colocated), database.engine
ls tests tests/unit tests/integration vitest.config.ts pyproject.toml 2>/dev/null
grep -rnE "vi\.mock\('pg|jest\.mock|sqlsmock|DATA-DOG/go-sqlmock" --include=*.ts --include=*.go . 2>/dev/null | grep -v node_modules
grep -rnE "unittest\.mock" --include=*.py . 2>/dev/null | grep -iE "patch.*(session|engine|pool)"
grep -rnE "sleep\(" --include=*.ts --include=*.go --include=*.py tests src internal 2>/dev/null   # sleep-based waits
docker info >/dev/null 2>&1 && echo "docker: ok" || echo "docker: missing"
```

- The DB-mock grep (`vi.mock('pg`, `jest.mock`, `sqlsmock`, `DATA-DOG/go-sqlmock`, `unittest.mock` patching a session) is the main finding: those tests verify an assumption about the SQL, not the SQL. They move to real-Postgres integration tests.
- Sleep-based waits are the second finding: they are the flake source in CI.
- `docker info` failing means the container runtime is absent: the integration suites cannot run on this machine. Install everything; verify unit tests only, and say so.
- The support modules assume `database.engine: postgres` (`TRUNCATE` over `pg_tables`, the testcontainers Postgres module). Another engine means adapting the reset, not the pattern.

## 2. Decide

- No test config and no `tests/`: full setup, steps 3 to 7.
- Tests exist but mock the database or sleep: delta. Add the support modules from [test-tracks.md](test-tracks.md), move the DB-touching tests into the integration project or suite, replace sleeps with condition waits. Keep sound unit tests where they are.
- Vitest `projects` (unit, integration), Go `TestMain` with a container and the pytest `integration` marker all present, no DB mocks, step 7 green: report "already in place" and stop.

## 3. Detect track and layout

| `backend.track` | Test stack |
|---|---|
| `hono` | Vitest 5 `projects`: `unit`, `integration` |
| `go` | `go test`: unit tests beside the code (`*_test.go`), integration and HTTP tests in `tests/` |
| `fastapi` | pytest with `tests/unit`, `tests/integration`, marker `integration` |
| `nextjs` | Stop. Use [build-nextjs-backend](../build-nextjs-backend/SKILL.md). |
| `supabase` | Stop. Use [secure-supabase-rls](../secure-supabase-rls/SKILL.md); its test side is [rls-test-patterns.md](../secure-supabase-rls/rls-test-patterns.md). |
| `none` or unset | Run `set-up-stack-profile`, then a `scaffold-*-service` skill. |

`tests.layout` decides where unit tests live: `tests-dir` (default) -> `tests/{unit,integration,support}`; `colocated` -> TS unit tests in `src/**/*.test.ts` (change the unit project's `include`), integration stays in `tests/integration`. Go keeps unit tests beside the code whatever the profile says ([service-layout.md](../_shared/service-layout.md)). The worked example is the `documents` module of [set-up-backend-auth](../set-up-backend-auth/SKILL.md); rename it or delete it once a real module uses the shape.

## 4. Install only what is missing

```bash
pnpm add -D vitest @testcontainers/postgresql   # hono; add @types/pg too when the app uses pg
go get github.com/testcontainers/testcontainers-go/modules/postgres github.com/go-jose/go-jose/v4   # go: container + test-token signing
uv add --dev testcontainers "psycopg[binary]"     # fastapi: plain testcontainers, no [postgres] extra
```

Python imports the container from `testcontainers.community.postgres` (`testcontainers.postgres` is deprecated). `psycopg` is the one driver: the container URL (`postgresql+psycopg://`) drives the migration pass and the app engine unchanged — no URL rewriting between them. Use the profile's package manager. Versions: [stack-versions.md](../_shared/stack-versions.md).

## 5. Generate

Create from [test-tracks.md](test-tracks.md), skipping files that exist:

```
tests/support/      container start, db reset, IdP tokens, app builder, factories
config              vitest projects / pytest markers (Go needs none: TestMain)
first unit test     a policy or pure service function
first integration   one resource through HTTP against real Postgres
```

The container is migrated with the repo's real migrations — [set-up-database](../set-up-database/SKILL.md) owns them; the worked example ships its own `drizzle/0000_init.sql` / `migrations/0001_init.sql`.

Scripts — unit tests are the fast loop, integration joins on demand or in CI:

- TS `package.json`: `"test": "vitest run"`, `"test:unit": "vitest run --project unit"`, `"test:integration": "vitest run --project integration"`.
- Go: `go test ./internal/...` for the fast loop; `go test ./...` for all of it, `tests/` included.
- Python: `uv run pytest -m "not integration"` for units; `uv run pytest` for everything.

## 6. Wire into CI and contract checks

One job per track: unit tests first (fail fast), then integration in the same job — the `ubuntu-latest` runners have Docker, and testcontainers starts its own Postgres, so no service container is needed. The workflow with SHA-pinned actions is in [test-tracks.md](test-tracks.md) ("CI job").

Contract tests, two kinds:

1. **Spec drift.** Generate the OpenAPI document and compare with the committed copy at `contracts/openapi.json`; fail the step when they differ. TS: `pnpm openapi` (via `scripts/print-openapi.ts`), then `diff openapi.json contracts/openapi.json`. Python: dump `app.openapi()`. Go: `openapi.yaml` is hand-written or the oapi-codegen source, so drift is a regenerate-and-diff. The Hono scaffold git-ignores `openapi.json`; that is why the committed copy uses the `contracts/` name.
2. **Conformance.** Run schemathesis against the running app:

```bash
schemathesis run http://localhost:PORT/openapi.json --checks all -n 25 --exclude-path '/documents/{doc_id}'
```

Verified on the FastAPI scaffold: 55 generated, 55 passed — but only after the first run found a real bug (a 405 without an `Allow` header). Protected endpoints: add `-H "Authorization: Bearer $TOKEN"`. In CI, start the app in the background and pass `--wait-for-schema 30`.

## 7. Verify

```bash
pnpm test       # hono
go test ./...   # go
uv run pytest   # fastapi
```

Expected: all green. The worked example: 3 unit files + 1 integration file (TS), and 6 integration cases per track — own tenant 200, other tenant 404, missing scope 403, no token 401, wrong audience 401, malformed id 400; the Go and Python files add an expired-token 401 case. The first integration run pulls the `postgres:18` image and is slower; with the image cached, the full suite took 15-16 s locally. A second run of this skill changes nothing.

## References

- [test-patterns.md](test-patterns.md): the pyramid, real Postgres over mocks, HTTP-level tests, fakes, naming, CI rules, what not to test.
- [test-tracks.md](test-tracks.md): every file for Vitest, go test and pytest, plus the CI job.
- [auth-patterns.md](../set-up-backend-auth/auth-patterns.md): the policies and tenant scoping these tests prove.
- [configure-test-stack](../../frontend/configure-test-stack/SKILL.md): the frontend side; browser-level tests live there.

Files in this skill

  • SKILL.md7.7 KB
  • test-patterns.md8.5 KB
  • test-tracks.md31.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.