Test Generator
Write tests that actually catch bugs, not tests that just run. A test that only covers the happy path is a partial test.
Step 1 — Identify the target
If the user named a file or function, use that. Otherwise, ask:
- "What should I generate tests for? The current file, recently-changed code, or a specific function?"
Then read the target in full — function signature, body, and anything it depends on. Read callers too; they reveal the real-world inputs.
Step 2 — Detect the test setup
Before writing anything, figure out the repo's conventions:
- What test framework? (jest, vitest, pytest, go test, rspec, etc.) — check
package.json, pyproject.toml, existing *_test.* files.
- Where do tests live? (alongside source?
__tests__/? tests/?)
- What's the naming convention? (
foo.test.ts, test_foo.py, foo_test.go)
- What assertion style? (
expect().toBe(), assert foo == bar, etc.)
- How are fixtures/mocks handled? — look at an existing test file as a template.
If you can't tell, read 1–2 existing tests in the repo. Match their style exactly. A test file that looks alien will not be merged.
Step 3 — Enumerate cases before writing
List out what you'll test. Don't skip this step — it's what separates real coverage from theater.
For each function or behavior, think through:
- Happy path — the canonical case the function is written for.
- Boundary — empty input, single-element input, max-size input, zero, negative, very large.
- Invalid input — wrong type, null, undefined, malformed, out of range. What should happen — error? silent default? document it.
- Failure modes — if this calls a network/DB/filesystem, what happens when that fails? When it times out? When it returns garbage?
- Concurrency — if this has shared state, what happens under parallel calls?
- Idempotency — if this is meant to be safe to retry, does a second call break things?
Not every function needs all of these. Use judgment — a pure function doesn't need concurrency tests.
Present the list to the user before writing tests if there are more than ~6 cases. Let them cut scope.
Step 4 — Write the tests
- One behavior per test. If a test has multiple unrelated assertions, split it.
- Describe the behavior in the test name, not the mechanics.
returns empty array when input is empty beats test_empty.
- Arrange / act / assert — visually separate setup, the call, and the check. Blank lines are fine.
- Prefer real values over mocks. Mock only what you must (network, time, randomness). Over-mocked tests pass while prod breaks.
- Assert the specific thing you care about.
toBe(42) beats toBeTruthy().
- No shared mutable state between tests. Each test sets up what it needs.
Step 5 — Run them
Run the new tests and confirm they pass. If they don't, fix the tests (or the code, if the test revealed a real bug — flag this to the user). Report:
- How many tests you added
- Whether they pass
- Any case you didn't cover and why (e.g. "skipped concurrency case — would need refactoring the module to be testable")
Anti-patterns to avoid
- Tests that just re-run the function and check the return value matches what the function returns. (A test must encode the expected behavior independently.)
- Tests that mock the function being tested.
expect(foo).toBeDefined() as the only assertion.
- Deleting or weakening assertions to make a failing test pass.
- Snapshot tests for anything other than deterministic, stable output.
1---2name: test-gen3description: This skill should be used when the user types /test-gen, asks to write tests for a file or function, or says their code lacks coverage.4---56# Test Generator78Write tests that actually catch bugs, not tests that just run. A test that only covers the happy path is a partial test.910## Step 1 — Identify the target1112If the user named a file or function, use that. Otherwise, ask:1314- "What should I generate tests for? The current file, recently-changed code, or a specific function?"1516Then read the target in full — function signature, body, and anything it depends on. Read callers too; they reveal the real-world inputs.1718## Step 2 — Detect the test setup1920Before writing anything, figure out the repo's conventions:21221. What test framework? (jest, vitest, pytest, go test, rspec, etc.) — check `package.json`, `pyproject.toml`, existing `*_test.*` files.232. Where do tests live? (alongside source? `__tests__/`? `tests/`?)243. What's the naming convention? (`foo.test.ts`, `test_foo.py`, `foo_test.go`)254. What assertion style? (`expect().toBe()`, `assert foo == bar`, etc.)265. How are fixtures/mocks handled? — look at an existing test file as a template.2728If you can't tell, read 1–2 existing tests in the repo. Match their style exactly. A test file that looks alien will not be merged.2930## Step 3 — Enumerate cases before writing3132List out what you'll test. Don't skip this step — it's what separates real coverage from theater.3334For each function or behavior, think through:3536- **Happy path** — the canonical case the function is written for.37- **Boundary** — empty input, single-element input, max-size input, zero, negative, very large.38- **Invalid input** — wrong type, null, undefined, malformed, out of range. What *should* happen — error? silent default? document it.39- **Failure modes** — if this calls a network/DB/filesystem, what happens when that fails? When it times out? When it returns garbage?40- **Concurrency** — if this has shared state, what happens under parallel calls?41- **Idempotency** — if this is meant to be safe to retry, does a second call break things?4243Not every function needs all of these. Use judgment — a pure function doesn't need concurrency tests.4445Present the list to the user *before* writing tests if there are more than ~6 cases. Let them cut scope.4647## Step 4 — Write the tests4849- **One behavior per test.** If a test has multiple unrelated assertions, split it.50- **Describe the behavior in the test name,** not the mechanics. `returns empty array when input is empty` beats `test_empty`.51- **Arrange / act / assert** — visually separate setup, the call, and the check. Blank lines are fine.52- **Prefer real values over mocks.** Mock only what you must (network, time, randomness). Over-mocked tests pass while prod breaks.53- **Assert the specific thing you care about.** `toBe(42)` beats `toBeTruthy()`.54- **No shared mutable state between tests.** Each test sets up what it needs.5556## Step 5 — Run them5758Run the new tests and confirm they pass. If they don't, fix the tests (or the code, if the test revealed a real bug — flag this to the user). Report:5960- How many tests you added61- Whether they pass62- Any case you *didn't* cover and why (e.g. "skipped concurrency case — would need refactoring the module to be testable")6364## Anti-patterns to avoid6566- Tests that just re-run the function and check the return value matches what the function returns. (A test must encode the *expected* behavior independently.)67- Tests that mock the function being tested.68- `expect(foo).toBeDefined()` as the only assertion.69- Deleting or weakening assertions to make a failing test pass.70- Snapshot tests for anything other than deterministic, stable output.