Write unit tests for code
Get tests that earn their place: behavior-driven cases, edge cases named before they're coded, and no tests that merely restate the implementation.
Last updated
Fill in the blanks
Assembled in your browser — nothing you type is stored or sent anywhere.
Your prompt
Model-agnostic — works in Claude, ChatGPT, Gemini or any AI chat.
Why this prompt works
- →Listing behaviours before writing tests is what stops the default failure: three near-identical happy-path tests and no edge cases.
- →"Public behaviour, not implementation" keeps the tests alive through refactors — tests coupled to internals fail when code improves, and teams learn to delete them.
- →The TODO-instead-of-guessing rule surfaces ambiguity as a question to you, rather than silently baking one interpretation into the test suite.
Part by part
- Before writing any test, list the behaviours worth testing
- Enumerate first, implement second. Going straight to code produces tests for whatever the model noticed first, with no way to see what it missed.
- edge cases (empty input, boundaries, invalid types)
- Three specific categories rather than a request for thoroughness, which is what makes the list checkable against the code.
- a name that states the expected behaviour
- A test name is documentation and a failure message. Named for the behaviour, a red test tells you what broke without opening it.
- Test the public behaviour, not the implementation details
- The rule that decides whether the suite helps or obstructs. Tests asserting on internals fail on every refactor and prove nothing about correctness.
- mark it with a TODO comment asking me
- The alternative to guessing intent, which produces a test that locks in behaviour nobody decided on.
Common mistakes
Write unit tests for this function.Produces tests for the happy path and one obvious edge case, with no list to check against. What is missing is invisible, which is the worst property a test suite can have.
Write comprehensive tests with 100% coverage.Coverage is a measure of lines executed, not of behaviour verified. Chasing it produces tests that call code without asserting anything meaningful, and a green number over an untested system.
Write tests that make sure this code works.Tests written from the implementation assert what the code does rather than what it should do, so a bug is faithfully encoded as expected behaviour and locked in.
Questions
Why list the behaviours first?
Because it makes the gaps visible. A list you can read against the code shows what was not covered, whereas a set of finished tests only shows what was.
What counts as an implementation detail?
Anything a caller cannot observe — which internal method was called, how many times, in what order. If you could rewrite the internals and the behaviour would be identical, the test should still pass.
Will the tests catch real bugs?
They catch regressions well and original bugs less often, because they are written from the same understanding as the code. That is precisely why the ambiguity markers matter more than the passing tests.
Should generated tests be reviewed?
As carefully as the code. A test asserting the wrong expected value is worse than no test — it fails when someone fixes the bug, and the natural response is to change the test.