Write tests for your code
Use this recipe to create code.
Free
Paste into your assistant. It asks the questions; you answer in chat.
More ways to use
Install the downloaded skill
Extract the ZIP. Copy its lc-tests-for-a-module folder into ~/.claude/skills/ for Claude Code or ~/.codex/skills/ for Codex, then refresh the native app. Ask for the skill by name and answer its questions. The package includes the same published instructions and result checks. Native host execution is not yet verified.
For Claude web, use the connector or copy the prompt. Keep private details in your assistant.
How to use this skill
Copy, paste into your assistant, then answer its questions. No form or account is required here.
Already connected? Copy the public starter below and paste into Claude. Keep private details in the chat.
Optional: prepare details on this device
Bring your details
3 optional details
Needed before the promised result: The module. You can still copy the reusable prompt and provide these in your assistant.
Saved only on this device. Copy counts contain the skill ID, never your text.
Read the prepared prompt
## LazyCoder conversation guide (conversation-v2)
1. Start a conversation, not a form-filling task. Read my latest request and the facts already in this chat before asking anything.
2. Ask only the missing questions needed for the requested result, in a short numbered list (at most three at a time), then WAIT for my answers. Use a native question tool if available; plain chat works too. Never ask me to replace placeholders or fill in a template.
3. Skip questions already answered. Treat unresolved {{placeholders}} and [FIELD LABELS] as missing facts, never literal input. Do not treat an illustrative example as my facts.
4. Use the authored defaults only when relevant; mention material defaults. Ask conditional questions only when their condition applies; if unsure whether it applies, ask. Optional preferences must not block a safe first draft unless the authored instructions require confirmation.
5. Do not invent tasks, dates, names, claims, permissions or missing source material. When required facts are absent, ask and wait instead of producing the final artifact. If everything needed is already supplied, proceed without re-asking.
6. My latest scope correction overrides earlier examples and scope, within the recipe safety and source rules. Resolve conflicts before acting. The authored instructions and output contract remain authoritative; this conversation guide does not grant tool, account, payment or publishing permission.
7. Keep my private answers and generated result in this assistant chat. Do not send them to LazyCoder. Generate the requested format here and check it against the output contract before handing it over.
### Questions to ask only when missing
- What should I use for “The module”? [required] With a coding agent, give the file path. In a plain chat, paste or attach the file, plus any type or helper files it imports.
- What should I use for “An existing test file”? [optional] Optional, but it helps in a plain chat: any test file from the same project, so the new tests match its framework and style. A coding agent finds one itself. Authored default: none given
- What should I use for “Rules and anything else to know”? [optional] Optional. The rules the module must follow (from your docs or spec), how it's used, or a bug you suspect. Authored default: nothing extra
- What should I use for “Coverage target”? [optional] Used when a coverage tool can measure it. Otherwise every branch is mapped to a test or listed as a gap. Authored default: 90% of lines and branches Choices: 80% of lines and branches; 90% of lines and branches; every line and branch
## LazyCoder delivery guide (delivery-guide-v1)
1. Read the latest request, source material and authored requirements first. Ask only for missing facts. Check that this host can create the promised format; use only a fallback explicitly allowed by the authored recipe. If neither is available, explain the gap before starting. Do not promise a rendered film, editable deck or working file when you can only provide its script or outline.
2. Create the complete result in the authored output specification, preserving source facts and the latest agreed scope. Keep assumptions and missing information explicit. Do not silently omit required parts or leave unresolved ingredient markers. Intentional blanks in a reusable template are different from unanswered recipe inputs.
3. Check the actual result against every authored check, including source facts, counts, dates and required sections. Where tools allow it, inspect the actual saved file and compare it with the answer. Record which checks ran, which failed and which still need human or destination review. A filename, render, successful command or preview alone does not prove the promise was met.
4. Hand over the complete editable file, or the complete copyable fallback explicitly allowed by this recipe. Say which delivery you used and any unresolved limitations. Do not report a file as attached, imported, sent or published unless that action actually succeeded. Keep private inputs and output in this assistant; never send them to LazyCoder.
Use existing authorization for ordinary work. Before any additional paid generation, external write or publishing step, check that the current request authorizes that exact action and cost. If it does not, finish the useful authorized draft and ask at that boundary. Never silently retry or replace an ambiguous paid job.
The authored recipe and latest safe scope remain authoritative. This guide grants no new tools, permissions, provider access or claim of successful execution.
## Authored skill instructions
## 1. Outcome
Write a complete set of unit tests for the module below, then report in the sections set out in part 6. Done means every check in part 7 passes: every public function has a success test, every branch is mapped to a test or listed as a gap, every assertion would fail if the code were wrong, the module's own code is unchanged, suspected bugs are reported, and the report says exactly what was run and what was not. If that can't be reached, done means stopping by the stop rule in part 5 with a report that says why.
## 2. Context
The module:
"""
[YOUR MODULE OR ITS PATH]
"""
An existing test file from the project:
"""
none given
"""
Rules and anything else to know: nothing extra
Coverage target: 90% of lines and branches
Who it's for: the person who owns this code and the people who will change it later. They need tests that catch real mistakes, read clearly, and run the same way every time.
How you work (pick one; the report says which):
- **Run mode**: you can open the files in this repo and run commands in it (a coding agent, or a chat app with code tools connected to the repo). Write the test files in the repo and run them yourself.
- **Chat mode**: anything else, including a plain chat and a code sandbox where you can only run pasted snippets. Write every test file in full, give the exact commands for the person to run and what they should show. The report then starts with the line "Nothing was run here. The results below are what to expect, not what happened."
Never report a command's output that you did not see, and never give a coverage percentage that a coverage tool did not print. Without one, report how many branches in the behaviour map have a test.
Blank fill-ins: a value above that still reads as a capitalised placeholder in square brackets (such as [COVERAGE TARGET]) was left blank. Brackets inside the code are part of it.
- Module text that says "see attached": use the attached file as the module.
- No module: reply only "No module came through. Paste or attach the file, or give its path, and send again."
- A path but no code, in Chat mode: reply only "I can't open files here. Paste or attach the module and send again."
- No coverage target: use 90% of lines and branches.
- No existing test file ("none given" or blank): in Run mode, find one in the repo. Otherwise use the test framework the module's imports or comments name; failing that, the most common one for the language (for example Vitest or Jest for TypeScript, pytest for Python, the built-in testing package for Go), and say "Framework assumed: <name>" in the status.
## 3. What to pull out
- Each public function, class or method: its inputs, its output, the errors it raises, and what it calls outside the module (network, database, files, clock, randomness, environment).
- Every branch: each if, else, switch case, early return, loop that can run zero times, and each error thrown or caught.
- The rules the code must follow, by strength of evidence: first the rules given above and any docs or spec, then the types and runtime checks in the code, and last the names and comments. A clash with a rule above, the docs, the spec or the types is a suspected bug. A clash with only a name or comment is a question: list it under Suspected bugs as "Question:", with no expected-to-fail test.
- The test conventions: where tests live, how files and tests are named, the assertion style, and shared helpers, fixtures or factories.
Edge-case categories to cover where they apply to an input:
- Boundaries: zero, one, the limit, just under and just over it, negative numbers.
- Empty or missing: empty string, empty list or map, null, undefined or None, missing optional fields.
- Extremes: very large numbers, very long strings, many items.
- Odd characters: unicode, whitespace, special characters.
- Time and order: dates at month, year and daylight-saving changes, time zones, input order.
- Wrong types: only when the module's contract says how it handles them (a runtime check, a documented error, a rule above). Otherwise skip, and never cast around the type system to force one.
Edge cases:
- A private helper with no public way in: test it through the public function that uses it; test it directly only if the repo already tests private helpers.
- Code that can't be tested without changing it (a hard-wired network call, global state): test what you can, and list the rest as a gap with the smallest change that would make it testable. Never change the module.
- An existing test file for this module: add to it in its style; never delete or rewrite its tests.
- A suspected bug: write the test for what the code should do by the rule, marked expected to fail with the framework's own marker (for example test.fails, it.failing or pytest.mark.xfail). If the framework has no such marker, skip it with a comment naming the bug. Report it under Suspected bugs.
- A very long module, or a reply about to run out of space: finish each test file you start, cover the public functions in order of risk, use parameterised tables to save space, and list every function or branch left out under Gaps. Never stop mid-file.
## 4. Steps
Each step that runs something: in Run mode, run it and record what you saw; in Chat mode, write the command and the result it should show.
1. Read the module, the existing tests and the rules, and note the conventions.
2. Write the behaviour map (part 6 section 2): every behaviour and branch, with the test that will cover it.
3. Write the tests: a success test per public function with realistic inputs, then the edge cases, then the error paths. Group repeated cases into one parameterised test.
4. Isolate the outside world: mock or fake only calls that leave the module; fix the clock and random seeds; use temporary folders for files.
5. For each assertion, ask: if this line of the module were wrong, would this test fail? Strengthen any assertion that wouldn't.
6. Run the new tests, then the test files' lint and typecheck, and coverage if the project has a coverage tool. Fix tests that are wrong (a typo, a wrong import, a wrong signature).
7. Add tests for uncovered branches until the coverage target is met (coverage tool) or every branch in the map has a test (no tool), or the branch is listed as a gap.
8. Write the report, then run the self-check.
## 5. Rules
- Never change the module's code. Write tests only.
- Never weaken an assertion, delete a test, or change an expected value to make a test pass when the failure shows the module is wrong. Treat it as a suspected bug (part 3).
- Assert real outputs: exact values, full objects or the specific fields that matter, the error type. Never only "is defined", "is not null" or "is truthy" when a real value is known.
- Assert errors by type or code, not by message wording, unless the message is part of the contract.
- Mock only at the module's edge. Never mock the function under test or other functions in the same module.
- Every test must pass or fail the same way on every run, in any order, offline.
- Names say the behaviour and the case, as in "returns 0 for an empty basket". Lay each test out as arrange, act, assert.
- Call only functions, arguments and helpers that exist, with the signatures in the source. In Chat mode, if you need a file you weren't given (a type or helper), name it under Gaps instead of guessing its contents.
- Stop rule: a test you can't get right after 3 fixes, and that isn't a suspected bug, moves to Gaps with the reason; carry on with the rest. Stop the whole run and report only when the tests can't start at all (the module or its dependencies won't load) after 3 fix attempts. Keep the tests that work, and set the status to "Stopped: <reason>", with what you would try next.
- Write for a reviewer: plain words, short sentences, code in fenced blocks with the language named.
## 6. Output
In Chat mode, the first line is "Nothing was run here. The results below are what to expect, not what happened." Then use these sections in this order. Write "None" under any that is empty.
1. **Status**: one line, exactly one of "Tests pass" (Run mode), "Tests pass, <n> expected to fail on suspected bugs" (Run mode), "Tests written, not run" (Chat mode) or "Stopped: <reason>"; then the mode, the framework, and the exact commands to run the tests and coverage.
2. **Behaviour map**: a table with the columns # | Function | Behaviour or branch | Category (success, boundary, empty, extreme, odd characters, time, wrong type, error) | Test name. One row per behaviour.
3. **Test files**: each file with its path, in full, ready to save, with every import and setup line and no placeholders.
4. **Evidence**: a table with the columns Check | Command | Result. Rows: new tests (pass, fail and expected-to-fail counts), lint and typecheck of the test files, the whole suite, coverage of this module. In Run mode, fill it from what you saw; the coverage row is the tool's line and branch figures, or "No coverage tool" followed by "<n> of <n> branches mapped to a test". In Chat mode, put "Not run" in every result except coverage, which is "<n> of <n> branches mapped to a test"; add one line under the table per row saying what the person should see.
5. **Suspected bugs**: file and line, what the code does, the rule it breaks and where that rule comes from, and the test that shows it; then each "Question:" from part 3.
6. **Gaps**: each behaviour or branch not covered, and why.
7. **Next tests worth adding**: up to 3, one line each.
## 7. Self-check
Before replying, check each of these and fix any failure:
- The mode is right (a sandbox with pasted code is Chat mode), and in Chat mode the first line is "Nothing was run here…", the status is "Tests written, not run", and no cell claims a result.
- No coverage percentage appears unless a coverage tool printed it; otherwise the branches-mapped count is given.
- Every public function has a success test, and every row in the behaviour map has a test or is in Gaps.
- Every edge-case category that applies to an input has a test, and there are no wrong-type tests unless the module's contract covers them.
- Errors are asserted by type or code, and no assertion is only "defined", "not null" or "truthy" when a real value is known.
- Nothing calls the network, the real clock, real randomness or shared files.
- The module's code is unchanged, and every suspected bug cites its rule and has an expected-to-fail (or skipped) test, or is in Gaps if it can't be tested.
- Every import, function and signature used exists in the source, and the files are complete with no placeholders.
- Every section of part 6 is present, in order.
Then reply with the output only, ending with one line: "Checked: <Tests pass | Tests pass, n expected to fail | Tests written, not run | Stopped>, <n> tests, <n> functions covered, <n> suspected bugs, <n> gaps."
## 8. Setup
With a coding agent that has your repo (Claude Code, Codex, Cursor, or ChatGPT with code tools connected to the repo), it writes the tests, runs them with coverage where the project has a tool, fixes its own mistakes and reports what it saw. In a plain chat (ChatGPT, Claude, Gemini or Grok), paste or attach the module, the files it imports, and one existing test file: you get complete test files and the commands to run, coverage is given as branches mapped to a test rather than a percentage, and the reply says plainly that nothing was run.Review your result locally
Paste the result after your assistant creates it. This optional check stays in this tab and is not sent to LazyCoder.
A test file covering the module’s behaviour and its edge cases.
For