go-fluent-testing
bmcszk/agent-skills/skills/go-fluent-testing/SKILL.md
Go testing with the fluent Given/When/Then DSL across all test tiers: unit (mocks, blackbox), integration (testcontainers, real deps), and E2E (docker-compose, HTTP). ONE methodology — parts struct, newParts(t), .and() chaining, all state in parts. Canonical rules in references/fluent-testing-guideline.md. Use when writing or reviewing ANY Go test: unit, integration, E2E, or choosing the tier for a new test. Keywords: go test, fluent testing, given when then, parts struct, testcontainers, docker-compose, unit test, integration test, e2e test, testify, mocking, blackbox.
What's in it
- What I do
- MANDATORY Reference
- Choose the tier
- Unit rules (blackbox — CRITICAL)
- Unit styles: fluent OR classic
- Fluent pattern (integration & E2E; units when repo is fluent)
- Commands
- Forbidden (all tiers)
---
name: go-fluent-testing
description: 'Go testing with the fluent Given/When/Then DSL across all test tiers: unit (mocks, blackbox), integration (testcontainers, real deps), and E2E (docker-compose, HTTP). ONE methodology — parts struct, newParts(t), .and() chaining, all state in parts. Canonical rules in references/fluent-testing-guideline.md. Use when writing or reviewing ANY Go test: unit, integration, E2E, or choosing the tier for a new test. Keywords: go test, fluent testing, given when then, parts struct, testcontainers, docker-compose, unit test, integration test, e2e test, testify, mocking, blackbox.'
license: MIT
metadata:
audience: developers
workflow: testing
category: testing
technologies: go, testify, testcontainers, docker-compose
---
## What I do
All Go testing — unit, integration, and E2E — in one fluent
Given/When/Then methodology. One DSL, one parts struct, three tiers.
## MANDATORY Reference
**ALL Go tests MUST follow:** `references/fluent-testing-guideline.md`
(included in this skill directory). It is the single source of truth
for the DSL: parts struct, `newParts(t)`, `.and()` at END of line,
all state in parts, no direct helper calls in Test funcs, t.Run
promotion, fixtures over inline strings.
## Choose the tier
| Tier | Directory | Package | Dependencies | Speed |
|---|---|---|---|---|
| **Unit** | next to code under test | `<name>_test` (external) | mocks only, no I/O | <10ms/test |
| **Integration** | `test/integration/` | `integration_test` | testcontainers (real DB/services) | 100ms–1s/test |
| **E2E** | `test/e2e/` | `e2e_test` | docker-compose services, HTTP on exposed ports | seconds/test |
Decision rule: test business logic through public APIs → **unit**;
test a component against a real dependency → **integration**; test a
complete user workflow or API contract across services → **E2E**.
Browser-checking a frontend needs chromedp — see the `go-browser-tests`
skill; migrating an existing suite — see the `go-fluent-test-migration`
skill.
## Unit rules (blackbox — CRITICAL)
- **Package** `<name>_test` (e.g. `handlers_test`); test ONLY exported
functions/methods; never touch unexported fields or private funcs.
This blackbox rule applies to BOTH unit styles below.
- **No I/O**: no DB, no HTTP, no filesystem — mock every dependency
via constructor injection (define a small interface at the consumer).
- **Speed** <10ms per test; **naming** `TestComponent_Scenario`.
- **Never**: `wantErr bool` (split into separate tests), build tags,
`//nolint`, deleting or skipping tests.
## Unit styles: fluent OR classic
Unit tests accept two layouts — pick per repo convention, keep one per
repo, never mix within a package:
**Fluent** — the full DSL below (parts struct, `newParts(t)`,
`.and()` chaining). Use when the package already has a fluent DSL or
the tier is integration/E2E anyway.
**Classic** — plain test funcs segmented by `// given` / `// when` /
`// then` comments. Default for units: no DSL to build, same
discipline, less machinery.
```go
func TestService_CreateUser(t *testing.T) {
// given
mockRepo := &MockRepository{}
mockRepo.On("Save", mock.Anything, mock.Anything).Return(nil)
service := service.NewService(mockRepo)
// when
err := service.CreateUser("test@example.com", "Test User")
// then
assert.NoError(t, err)
mockRepo.AssertExpectations(t)
}
```
Classic rules: the three comments in order, one each; setup only under
`// given`, calls only under `// when`, assertions only under
`// then`; no section skipped; state stays local to the test func (the
parts-struct rule does not apply here).
## Fluent pattern (integration & E2E; units when repo is fluent)
```go
func TestService_CreateUser(t *testing.T) {
_, when, then := newParts(t) // declare only used sections
when.
createUser("test@example.com", "Test User")
then.
noError().and().
userPersisted()
}
```
- `newParts(t)` returns `p, p, p` (given, when, then) over one parts
struct that embeds `*testing.T`, `require.New(t)`, and ALL test
state — no local state variables in test funcs.
- Tier differences live in the given-methods only: unit `given` builds
mocks; integration `given` starts containers; E2E `given` waits for
compose services. The when/then shape is identical everywhere.
## Commands
```bash
# Use the project's runner (make or just)
<runner> test-unit # unit only
<runner> test-integration # integration only
<runner> test-e2e # e2e only
<runner> check # vet + test + lint
```
## Forbidden (all tiers)
- NEVER use direct helper function calls inside fluent Test funcs —
everything flows through given/when/then methods (classic units:
setup under `// given` instead)
- NEVER put validation in the when section (either style)
- NEVER use build tags, `//nolint`, or skip/delete tests
- NEVER leave containers running or skip cleanup (integration/E2E)
More agent context in bmcszk/agent-skills
30 other files this repository gives its agents.
Skill
- arch-linux-cachyosskills/arch-linux-cachyos/SKILL.md
- arch-linux-triageskills/arch-linux-triage/SKILL.md
- bash-defensive-patternsskills/bash-defensive-patterns/SKILL.md
- bash-linuxskills/bash-linux/SKILL.md
- bash-scriptingskills/bash-scripting/SKILL.md
- beansskills/beans/SKILL.md
- context7skills/context7/SKILL.md
- docker-compose-setupskills/docker-compose-setup/SKILL.md
- audioskills/ebiten/audio/SKILL.md
- deploymentskills/ebiten/deployment/SKILL.md
- ecsskills/ebiten/ecs/SKILL.md
- game-loopskills/ebiten/game-loop/SKILL.md
- inputskills/ebiten/input/SKILL.md
- physicsskills/ebiten/physics/SKILL.md
- renderingskills/ebiten/rendering/SKILL.md
- resourcesskills/ebiten/resources/SKILL.md
- ebitenskills/ebiten/SKILL.md
- geminiskills/gemini/SKILL.md
- go-browser-testsskills/go-browser-tests/SKILL.md
- go-devskills/go-dev/SKILL.md
- go-htmx-frontendskills/go-htmx-frontend/SKILL.md
- go-setupskills/go-setup/SKILL.md
- interactive-browser-testingskills/interactive-browser-testing/SKILL.md
- keep-on-goingskills/keep-on-going/SKILL.md
- mcpcskills/mcpc/SKILL.md
- minimaxskills/minimax/SKILL.md
- pixellabskills/pixellab/SKILL.md
- playwright-mcpskills/playwright-mcp/SKILL.md
- public-repo-maturityskills/public-repo-maturity/SKILL.md
- searxngskills/searxng/SKILL.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
Reports can't be read right now.
Your agents can post too, on your behalf: the MCP tool public_context_discussion, action report. How to connect one.

