Generate Unit Tests With AI: A Prompt That Targets Untested Code First
Generate unit tests with AI using a reusable prompt that finds untested modules first and locks a per-module test plan. Copy the prompt and ship coverage.
Most teams already use AI to write code. Far fewer use it to write the tests that catch when that code breaks. The gap shows up the first time a refactor sails through a green suite that never actually checked the thing that changed.
The job here is narrow and repeatable: generate unit tests with ai for the modules that have none, in the framework you already use, with assertions that mean something. Do it once with a throwaway chat prompt and you get tests that pass on any output. Do it with a reusable prompt that locks the structure, and you get a suite you'd trust on a Friday deploy.
This post is the prompt and the workflow around it. Not a tool tour.
Why "write tests for this" produces tests that don't test
The default ask, pasted into ChatGPT or Claude, is some version of "write unit tests for this function." You get tests back. They run. Coverage ticks up. And half of them assert that the function returned something rather than the right thing.
GitHub's own guide to generating unit tests with Copilot leans on /tests plus careful prompting and suggests asking the model what it left untested. Useful, but it's IDE-bound and conversational. There's no reusable contract you can hand a teammate. QA prompt roundups like DeviQA's 50 ChatGPT prompts stay at the scenario level, payment flows and end-to-end journeys, not the unit-level assertions a developer needs before a merge.
The fix isn't a better model. It's a prompt with three jobs baked in: find what's untested, target it, and lock the output so every test file comes back the same shape.
What you can do with this prompt
- Point it at a module with zero coverage and get a complete test file in your framework.
- Rank a directory by which files carry the most untested logic, then generate for the top ones.
- Force a failing-input case per function so the tests assert behavior, not just execution.
- Re-run it after a refactor to fill the new gaps without rewriting the existing suite.
- Hand the same prompt to another developer and get tests in the same structure every time.
- Generate tests across Python, TypeScript, and Go from one contract by swapping a
{{language}}variable.
Anatomy of the prompt
The shape that survives long files puts the role first, the target context in the middle, and the output contract last. Models weight the most recent tokens, so the contract belongs at the end where it won't get buried under a 400-line paste.
Role: You are a test engineer writing unit tests in {{test_framework}}.
Context: {{source_code}} — the module under test.
{{existing_tests}} — tests already present (don't duplicate).
Task: Identify every public function with no or weak coverage.
For each, write tests: one happy path, one error/invalid input,
one boundary case. Assert the actual value, never just "not null".
Output: One fenced code block per module. Inside, arrange-act-assert
structure, descriptive test names, and a short comment naming
the behavior each test pins down.
A test that calls the function and checks it didn't throw isn't a test. It's a smoke check. The prompt's output contract forces a real assertion per case, plus one input the function should reject, because a suite that only feeds happy inputs passes right up until production doesn't.
Step-by-step usage
1. Gather inputs
Pull the source file and any existing tests. If you're targeting a whole directory, run your coverage tool first and paste the report so the prompt knows which files to prioritize. The test coverage gap workflow pairs well here for picking targets.
2. Fill variables
Set {{test_framework}} (pytest, Jest, Go's testing package), {{source_code}}, and {{existing_tests}}. Name the framework explicitly. "Write tests" without a framework gets you a guess, and the guess is usually whatever dominated the model's training data.
3. Run the prompt
Run it per module rather than dumping the whole repo. Smaller scope means the model holds more of the file in working context and skips fewer functions.
4. Post-process
Read every assertion. The most common failure mode: the model asserts the function returns the input unchanged when it should transform it. Fix the assertion, don't delete the test.
5. Iterate
If coverage is still thin, paste the new coverage report back and ask only for the gaps. Don't regenerate the passing tests.
Prompt-craft patterns that lock the output
Three example tests beat a paragraph of rules. Show the model three tests already in your exact target shape, same naming, same assertion style, and the format locks. Describe the shape in prose instead and it drifts by the third file.
Match this structure exactly:
def test_parse_date_accepts_iso_format():
assert parse_date("2026-06-14") == date(2026, 6, 14)
def test_parse_date_rejects_garbage():
with pytest.raises(ValueError):
parse_date("not-a-date")
Put the "don't duplicate" rule next to the existing tests. Drop {{existing_tests}} right above the instruction to skip them. Far from that context, the model re-writes tests you already have.
Name the framework version when behavior depends on it. Jest 29's toStrictEqual differs from older toEqual. Pin it so the generated tests don't use an assertion your runner doesn't have.
Here's the opinionated part. Don't chase 100% coverage with this prompt. Coverage percentage measures lines executed, not behavior verified, and the last 15% is usually getters, logging, and dead branches that cost more to test than they'll ever save. Generate tests for the logic that can break in ways a user notices. Skip the rest. A focused 70% that asserts real behavior beats a padded 95% of assert not None.
Variables you'll set
| Variable | Required | What it is |
|---|---|---|
{{test_framework}} | Yes | The framework and version, e.g. pytest 8, Jest 29 |
{{source_code}} | Yes | The module under test |
{{existing_tests}} | No | Tests already present, so the prompt skips them |
{{language}} | No | Set when generating across multiple languages |
{{coverage_report}} | No | Paste to prioritize the least-covered files first |
Getting started
- Pick one module with no tests, the riskiest one you can find.
- Run your coverage tool and copy the report.
- Fill
{{test_framework}}and{{source_code}}. - Add three example tests in your house style.
- Run the prompt per module, not per repo.
- Review every assertion before staging.
- Wire the prompt into a reusable pack so the next developer runs the same contract: the Polyglot Test Harness Scaffolder maps untested modules per language and scaffolds the suite.
The Polyglot Test Harness Scaffolder does this end-to-end: a {{language}} variable and a per-module output contract that maps every untested or under-tested file across a multi-service repo, then scaffolds tests in the right framework for each. It's part of The Complete AI Prompts Bundle, a one-time lifetime license to the whole catalog plus every pack added later, worth it if you run more than one of these jobs.
Once the suite exists, the next problem is keeping it honest. A green suite that never fails on bad code is its own kind of debt. Two reads worth your time: how to spot flaky tests before they erode trust, and a verification rubric for AI-written code so the tests and the code both get checked. If you want the test pack with a coverage-gap companion built in, the Test Coverage Gap Harness reads raw coverage output and tells you where to point this prompt next.
Find the gaps with the Coverage Gap Harness →Common questions
How do you generate unit tests with AI?
Which is better for generating unit tests, ChatGPT or Claude?
Will AI-generated unit tests actually catch bugs?
Get the prompt packs this guide is built on
Ready-to-paste prompts with documented variables and worked examples for ChatGPT, Claude, and Gemini. One-time payment, own it forever.
More prompt guides

An Agent Self-Reflection Prompt That Actually Catches Errors
The reflection pattern promises a tidy story: the agent writes something, looks at it critically, fixes the flaws, and ships better work. Sometimes it does. Often it writes a confident answer, grades…

RAG for a Codebase: Prompt Patterns That Stay Grounded
Ask a coding agent how authentication works in your repo and, without the right context, it'll describe a plausible auth system that isn't yours. Confident, well-structured, wrong. RAG for a codebase…

Multi-Agent Orchestration: Prompt Patterns That Scale
One agent can only hold so much in front of the model before it gets confused, expensive, or both. Split the work across several agents and a new problem appears: who decides what, who talks to whom,…