Writing tests is often the most skipped step in development — not because developers are lazy, but because articulating what to test takes more time than writing the test itself. These ready-to-use Test Writing Prompts eliminate that blank-page problem by giving LLMs the exact structure, edge cases, and assertions you need.
I've been using LLMs like Claude, GPT-4, Gemini, and DeepSeek to generate test suites for the past two years. The difference between a useless test and a valuable one isn't the model — it's the prompt. Generic "write tests for this function" prompts produce generic tests: happy path only, no edge cases, no mocking strategy. These Test Writing Prompts solve that by forcing the model to think like a QA engineer, not a code autocompleter.
Why Generic Prompts Fail Here
When you say "write tests for this," the LLM has to guess everything: your mocking framework, your assertion style, your coverage expectations. It defaults to the most common pattern it's seen — which is usually a shallow Jest test with one expect statement.
The real failure mode is threefold:
- No edge case enumeration — LLMs skip null inputs, empty arrays, and boundary values unless explicitly asked.
- No mocking strategy — they either mock everything (useless integration coverage) or nothing (flaky tests).
- No failure assertion — they test that things work, but rarely that things fail correctly.
These templates force the model to address all three explicitly.
Template 1: The "Contract First" Test Generator
This template works best when you have a function with clear input/output contracts. It makes the LLM enumerate the full input space before writing a single assertion.
You are a senior QA engineer. Generate a comprehensive test suite for the following function.
Function:
[PASTE FUNCTION CODE HERE]
Requirements:
1. List every input type the function accepts (valid, invalid, boundary, null, undefined).
2. For each input category, write exactly one test case using [TESTING_FRAMEWORK].
3. Mock all external dependencies using [MOCKING_LIBRARY]. List what you're mocking and why.
4. Include at least one test that asserts an error is thrown for invalid input.
5. For each test, write a one-line comment explaining the business rule it protects.
Output format:
- Input category list (3-5 items)
- Test code blocks
- Brief summary of coverage gaps you couldn't test
Framework: [JEST | VITEST | PYTEST | MOCHA]
Language: [TYPESCRIPT | JAVASCRIPT | PYTHON]
Placeholder breakdown:
[PASTE FUNCTION CODE HERE]— the actual function, not a description[TESTING_FRAMEWORK]and[MOCKING_LIBRARY]— be explicit, or the LLM picks randomly[TYPESCRIPT | JAVASCRIPT | PYTHON]— prevents syntax mismatch
Template 2: The "Failure Injection" Suite
This one targets the hardest part of testing: asserting that failures happen correctly. Most developers test the happy path; this template forces the LLM to design for breakage.
You are a chaos engineer writing unit tests. For the following function, generate tests that verify:
- Correct behavior when dependencies fail (timeout, 500 error, empty response)
- Correct behavior when data is malformed (wrong types, missing fields, extra fields)
- Correct error messages and error types for each failure mode
- That no side effects occur when the function fails (e.g., no DB writes, no API calls)
Function:
[PASTE FUNCTION CODE HERE]
Constraints:
- Use [TESTING_FRAMEWORK] with [MOCKING_LIBRARY].
- For each failure scenario, mock the dependency to throw a specific error and assert the function's response.
- Include a table of failure scenarios: dependency, mock behavior, expected result.
- Flag any failure modes you cannot test with the current architecture.
Output format:
- Failure scenario table
- Test code
- Architecture notes on testing gaps
Template 3: The "Integration Boundary" Probe
This is for the hardest case: testing functions that touch multiple systems. It's designed for edge cases that pure unit tests miss — race conditions, partial failures, and state consistency.
You are testing a function that coordinates multiple services. Generate an integration test suite that covers:
Function:
[PASTE FUNCTION CODE HERE]
Services involved:
[LIST SERVICES: DB, API, QUEUE, CACHE, etc.]
Required coverage:
1. Partial failure: Service A succeeds, Service B fails — assert the function's compensation logic.
2. Ordering: If the function relies on call order, test that out-of-order responses still resolve correctly.
3. Idempotency: Call the function twice with the same input — assert the second call doesn't duplicate side effects.
4. Concurrency: Simulate two simultaneous calls with overlapping data — assert no race condition corruption.
5. Timeout: Mock a slow dependency and assert the function respects its timeout threshold.
Use [TESTING_FRAMEWORK] with real service mocks (not stubs) via [MOCKING_LIBRARY].
For each test, specify:
- The exact mock setup
- The assertion that verifies correctness
- What would break in production if this test failed
Output format:
- Test code with setup/teardown
- A "production risk" note for each test
How to Adapt These for Your Own Codebase
The templates work out of the box, but they get dramatically better with three adjustments:
- Add your actual error classes — if your codebase throws
ValidationErrorinstead ofError, say so. LLMs default to generic errors. - Specify your test file naming convention —
function.test.tsvs__tests__/function.tschanges where the model puts things. - Include one real test from your codebase as an example — paste an existing test in the prompt. This anchors the LLM's style to your project's conventions.
I've also found that running the template twice with different models (e.g., Claude for edge cases, GPT-4 for mocking strategy) produces better coverage than running one model twice.
Do These Prompts Work With Any LLM?
Yes, but with caveats. Claude 3.5 Sonnet and GPT-4 handle these templates best — they follow multi-step instructions reliably. Gemini and DeepSeek work but may skip the "list your assumptions" step if you don't emphasize it. The key differentiator is context window: the templates require pasting the function plus the prompt, so models with smaller context windows (like some DeepSeek variants) may truncate the output. For best results, use these with models that have at least 64K context.
One thing that doesn't change across models: the quality of the output is directly proportional to the specificity of your placeholders. Vague inputs produce vague tests.
The One Adjustment That Improves These Prompts the Most
Add a single line to every template: "State three assumptions you made about this code that I should verify." This forces the LLM to surface its guesses about your architecture, naming, and dependencies. Nine times out of ten, one of those assumptions reveals a test you hadn't considered — and it's the fastest way to turn a generic test suite into one that actually protects your production code.