一键导入
itestkit
How to use `github.com/n-r-w/itestkit` to run integration tests from JSONC cases.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
How to use `github.com/n-r-w/itestkit` to run integration tests from JSONC cases.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | itestkit |
| description | How to use `github.com/n-r-w/itestkit` to run integration tests from JSONC cases. |
| metadata | {"version":"1.2"} |
<itestkit_usage> <when_to_use> Implementing data-driven integration tests at the API level. </when_to_use>
1. Unified case shape: 1) top-level `steps[]`: full execution pipeline (ordered) 2) top-level `assert`: expected outcome 2. JSONC + strict JSON: 1) comments are stripped 2) unknown fields are rejected 3) trailing JSON is rejected 3. Required fields: 1) case `name`: TrimSpace()'d, unique 2) `steps`: non-empty 3) each step: `id`, `kind`, `handler`, `request` 4) `assert.code`: parsed by `StatusCodec.Parse` 4. Allowed step kinds (full list): 1) `prepare` 2) `action` 3) `publish` 4) `await` 5) `verify` 6) `cleanup` 5. Await retry semantics: 1) `retry` is allowed only for `kind=await` 2) `retry` is required for `kind=await` 3) all retry fields must be positive: `timeout_ms`, `interval_ms`, `max_attempts` 6. Assert semantics: 1) `code == Success()`: - case execution must finish without unexpected error - `assert.response` is required - response comparison target: - `assert.response_from_step` when set - otherwise output of the last `action` step - `assert.response_mode` values: - absent or `exact`: compare full normalized expected and actual responses. Use `{ "$present": true }` as an object field value to assert that the field exists with any value. The marker is not valid at the root or as an array element. A present field with `null` passes. - `partial`: compare only fields present in `assert.response` - Semantic matcher objects are opt-in expected values in both modes: * `{ "$same_instant": "2026-05-30T10:00:00Z" }`: actual and expected values must be RFC3339 strings that represent the same instant. * `{ "$rfc3339": true }`: actual value must be an RFC3339 date-time string. Use it when only the format matters and the instant is dynamic. * `{ "$matches": "^trace-\\\\d+$" }`: actual value must be a string that matches the regular expression. * `{ "$not_empty": true }`: actual value must be a non-empty string, array, or object. `" "` passes; `""`, `[]`, `{}`, `null`, numbers, and booleans fail. - In `partial` mode: * object fields are recursive subsets, arrays require exact length and order, and scalar values use strict equality unless a semantic matcher object is used. * use `{ "$present": true }` with the same placement rules as in `exact`. * use `{ "$absent": true }` as an object field value to assert that the field is absent from the normalized actual response. A present field, including `null`, fails. * expected response stays as raw JSON after source preprocessing and is not decoded through `DecodeExpectedResponse`. - Actual response is normalized through `NormalizeResponse`. 2) `code != Success()`: - case must return an execution error - `ErrorInspector.FromError` must extract `(code, message)` - `assert.response` is ignored - `message_contains` is optional substring check for error message 7. Runner behavior details: 1) non-cleanup steps stop after first unrecoverable failure 2) cleanup steps still run in declared order after failure 3) step outputs are stored internally by `step.id` 8. Discovery: `*.jsonc` files are found recursively under rootDir and sorted; `LoadCases` fails if none found. 9. Runtime request templates: 1) Supported template forms in `steps[].request`: - `{{steps..response}}` - `{{steps..response.}}` 2) Template resolution happens during case execution, using outputs of already completed steps. 3) If template references an unavailable step output or invalid path, case execution fails on that step. 10. Runtime calendar macros in JSONC: 1) If cases use ``, ``, or ``, wrap `CaseSource` with `testcalendar.New().WrapSource(...)` before `itestkit.LoadCases(...)`. 2) `test_date` renders `YYYY-MM-DD`, supports day offsets only, and may use explicit fixed-offset timezone suffix `@±HH:MM`. 3) `test_timestamp` renders `YYYY-MM-DD HH:MM:SS+TZ`, supports day/hour/minute offsets in `d -> h -> m` order, and may use explicit fixed-offset timezone suffix `@±HH:MM`. 4) `test_rfc3339_timestamp` renders RFC3339, supports day/hour/minute offsets in `d -> h -> m` order, and may use explicit fixed-offset timezone suffix `@±HH:MM`. 5) The macro must occupy the whole string value; partial interpolation inside a larger string is not supported. 6) Inside `{"$date": ...}` only `test_date` is allowed; the selected calendar day may use explicit timezone, but the rendered value is RFC3339 midnight UTC. 7) Fixed anchor: `2026-03-01 03:00:00 +06:00`.<critical_requirements>
1. MUST use embed and itestkit.LoadCases to include case files in the binary for reliable access in all environments.
2. By default, JSONC fixtures MUST assert full API response bodies in assert.response, not only status codes or summaries. Use assert.response_mode: "partial" only when the project explicitly allows partial assertions and the selected fields prove the scenario behavior. Do not hardcode expected responses in test code.
3. MUST NOT create:
1) Custom data seeding in Go code instead of prepare step with jsonc request.
2) Custom API calls in Go code instead of action step with jsonc request.
3) Custom response checks in Go code instead of assert.response in jsonc.
Otherwise, you CAN'T expand test coverage without code changes.
5. MUST make code maintainable and readable:
1) Split jsonc cases by folders according to api handlers, queues, or features under test. Don't put all cases in one folder!
2) Split go code by files according to their role in the test suite: suite lifecycle, case harness factory, handlers, custom assertions, etc. Don't put all code in one file!
3) Make separate folders for different test suites if you have multiple: one suite per API, queue, etc.
4) Use helper functions and types to reduce boilerplate and improve readability. Don't repeat yourself in test code!
5) Add comments not only to code, but also to jsonc cases to explain each step's purpose and the overall test scenario. Don't make others guess your intentions!
6. If cases use calendar macros, MUST preprocess the source with testcalendar.New().WrapSource(...) before itestkit.LoadCases(...).
7. If your SUT computes time-dependent fields using its own clock, MUST pass testcalendar.FixedNow() into that clock too. WrapSource(...) changes fixtures only; it does NOT freeze the application's current time.
8. If integration tests require docker for their operations, all go files in integration test folders MUST have the build tag //go:build integration or similar.
</critical_requirements>
<minimal_example>
1. If your suite does not need shared heavy setup, use a no-op suite context (for example struct{}) and create per-case harnesses in NewCaseHarness.
2. For SQL DBs or MongoDB in tests, prefer github.com/n-r-w/testdock/v2.
3. For other external integrations (brokers, queues, caches, third-party services), prefer https://github.com/testcontainers/testcontainers-go and its modules.
4. If your handler registry is a simple static map without custom resolve logic, prefer itestkit.NewMapRegistry(...) to avoid boilerplate.
5. For strict decode of non-proto request and assert.response, prefer itestkit.DecodeStrictJSON(...) instead of duplicating local helpers.
6. For marker-aware JSON expectations inside custom handlers, decode raw expectation JSON with itestkit.DecodeExpectedJSON(...) and compare it with JSON-safe actual data through itestkit.MatchExpectedJSON(expected, actual, itestkit.MatchModeExact) or itestkit.MatchModePartial.
7. If your JSONC fixtures contain moving dates, prefer testcalendar.New().WrapSource(source) before LoadCases(...). If the SUT itself depends on current time, also inject testcalendar.FixedNow() into its clock. If fixtures use explicit timezone different from +06:00, normalize runtime timestamps to the same timezone before comparing them.
8. Optional execution options:
1) itestkit.WithContinueOnFailure()
2) itestkit.WithParallelCases()
3) itestkit.WithParallelismLimit(n)
4) env ITESTKIT_RESPONSE_DUMP="case name" runs one case by Case.Name, disables parallel execution, and always prints the normalized actual response JSON between dump markers without comparing it with assert.response. Command to extract dumped response:
bash (ITESTKIT_RESPONSE_DUMP="case name from jsonc" go test ... || true) 2>&1 \ | awk '/^[[:space:]]*ITESTKIT_RESPONSE_DUMP_BEGIN[[:space:]]*$/{dump=1; found=1; next} /^[[:space:]]*ITESTKIT_RESPONSE_DUMP_END[[:space:]]*$/{dump=0; next} dump {sub(/^ /, ""); print} END {if (!found) exit 2}' \ | jq . \ > /tmp/actual-response.json
Minimal (load + run with suite lifecycle):
go // 1) Provide integrations: // - itestkit.CaseSource (fs.ReadDirFS + fs.ReadFileFS) // - itestkit.HandlerRegistry[C] // - itestkit.SuiteLifecycle[SC] // - itestkit.SuiteCaseHarnessFactory[SC, C] // - itestkit.StatusCodec[S] + itestkit.ErrorInspector[S] // 2) Optional if fixtures use <test_date...>/<test_timestamp...>/<test_rfc3339_timestamp...>: // source = testcalendar.New().WrapSource(source) // 3) Load + run cases: cases, err := itestkit.LoadCases(source, "cases", registry, statusCodec) require.NoError(t, err) itestkit.RunCases(t, cases, lifecycle, caseFactory, errorInspector, statusCodec)
</minimal_example>
<ext_api>
How to test external API calls:
1. Keep external API behavior in service-specific handlers and case harness state; do not add transport-specific logic to itestkit core.
2. Use kind=prepare to:
1) seed DB/state
2) configure external request expectation
3) configure external stub response
3. Use kind=action to call your API/use-case that triggers outbound call.
4. Use kind=verify to check external side effects:
1) call happened
2) request payload matched
3) call count is expected
5. For eventual consistency use kind=await (with retry) before final verify/read step.
6. Set explicit assert.response_from_step for deterministic assert target.
</ext_api>
</itestkit_usage>