Skip to content
AI Dev Toolkit.
Esc
  • AI Token CounterCount tokens for GPT, Claude, Gemini, DeepSeek, Qwen and more.Tool
  • LLM API Cost CalculatorEstimate per-request, daily and monthly API costs.Tool
  • AI Model ComparisonCompare prices, context windows and features across models.Tool
  • AI Model Pricing PagesSpecs, real costs and cheaper alternatives for popular models.Tool
  • Context Window CheckerSee whether your text fits each model’s context window.Tool
  • Subscription vs API CalculatorFind out whether a chat plan or the API is cheaper for you.Tool
  • GPU / VRAM CalculatorCheck how much VRAM a local model needs and which GPUs fit.Tool
  • Prompt Caching CalculatorEstimate savings from prompt caching.Tool

Prompt template · Code

Prompt to write unit tests

Tests for normal cases, edge cases and errors in your framework, focused on behaviour rather than implementation. Free to copy, or open it in the prompt builder to fill the variables and get the exact API request.

268 tokens unfilled · 413 with the example below · o200k_base (GPT-4o and later)

Template

The prompt

Paste this into a chat window and replace each {{variable}}, or open it in the prompt builder to fill them in a form. Sections are marked with XML tags, which every major model reads well.

Prompt (one message, plain text)
You are a careful {{language}} engineer who writes tests that catch real regressions and survive refactoring.

<context>
The code under test:
<code>
{{code}}
</code>

An existing test in this project, to match its style and helpers:
<style_example>
{{existing_test}}
</style_example>
</context>

<constraints>
- Test behaviour through the public interface, not private helpers or the order of internal calls.
- Cover: the normal case, boundaries (empty, zero, one, maximum), invalid input and each error the code can raise or return.
- Mock only what crosses a process boundary, such as the network, the clock or the file system. Use real objects otherwise.
- Give each test a name that states the expected behaviour.
- If the code has a bug that a test exposes, keep the test, mark it clearly, and explain the bug after the code. Never weaken a test to make it pass.
</constraints>

<output_format>
One code block with the complete test file, ready to run. After it, a short list of any behaviour you couldn’t test without changing the code, and why.
</output_format>

<task>
Write unit tests for the code above using {{framework}}. Cover the behaviour a caller relies on, including edge cases and failure paths.
</task>
For an API call: system prompt and user message

With a system prompt, the role, constraints and output format stay fixed across calls (and can be cached), while the context and task go in the user message.

System prompt
You are a careful {{language}} engineer who writes tests that catch real regressions and survive refactoring.

<constraints>
- Test behaviour through the public interface, not private helpers or the order of internal calls.
- Cover: the normal case, boundaries (empty, zero, one, maximum), invalid input and each error the code can raise or return.
- Mock only what crosses a process boundary, such as the network, the clock or the file system. Use real objects otherwise.
- Give each test a name that states the expected behaviour.
- If the code has a bug that a test exposes, keep the test, mark it clearly, and explain the bug after the code. Never weaken a test to make it pass.
</constraints>

<output_format>
One code block with the complete test file, ready to run. After it, a short list of any behaviour you couldn’t test without changing the code, and why.
</output_format>
User message
<context>
The code under test:
<code>
{{code}}
</code>

An existing test in this project, to match its style and helpers:
<style_example>
{{existing_test}}
</style_example>
</context>

<task>
Write unit tests for the code above using {{framework}}. Cover the behaviour a caller relies on, including edge cases and failure paths.
</task>

Variables

What to put in each variable

{{language}}
The language, such as Python or TypeScript.
{{code}}
The function or module to test, including the types it uses.
{{existing_test}}
One existing test file or test from the project, so the output uses the same imports, fixtures and naming. Write “none” for a new project.
{{framework}}
The test framework and any helpers, such as pytest with hypothesis, or Vitest with Testing Library.

Use

When to use it

Use it to add tests to code that has none, to fill gaps before a refactor, or to get a first draft of edge cases you might not think of. It works best on pure functions and small modules with clear inputs and outputs; for code tangled with I/O, expect a list of things it couldn’t test, which is useful information in itself.

Read every generated test before committing it. A test that passes against buggy code just locks the bug in, so check that each assertion matches the behaviour you want, not just the behaviour the code currently has.

Structure

Why it’s structured this way

The existing test is in the context because style mismatches are the main reason generated tests get rewritten: wrong import paths, a different assertion library, fixtures that already exist being rebuilt by hand. One real example is usually enough to fix that.

The constraints encode what makes tests worth keeping. Testing through the public interface lets tests survive refactoring. The boundary list (empty, zero, one, maximum) is where bugs actually live. Limiting mocks to process boundaries avoids the common failure where every dependency is mocked and the test only proves the mocks were called.

The last rule matters most. Anthropic’s guide warns that models can focus on making tests pass rather than on correct behaviour, and suggests saying so explicitly; here, a test that exposes a bug is kept and reported instead of quietly bent to match the code.

Example

A filled-in example

{{language}}
Python
{{code}}
def parse_duration(text: str) -> int:
    """Parse "1h30m", "45s" or "2h" into seconds."""
    units = {"h": 3600, "m": 60, "s": 1}
    total, number = 0, ""
    for ch in text.strip():
        if ch.isdigit():
            number += ch
        elif ch in units and number:
            total += int(number) * units[ch]
            number = ""
        else:
            raise ValueError(f"bad duration: {text!r}")
    return total
{{existing_test}}
def test_slugify_lowercases_and_joins_words():
    assert slugify("Hello World") == "hello-world"
{{framework}}
pytest, with pytest.mark.parametrize for tables of cases

What to look for. A good test file includes a case for trailing digits with no unit, such as "90". The function returns 0 there instead of raising, which is a real bug that the “keep the test and explain it” rule should surface rather than hide.

The complete filled-in prompt (413 tokens)
Filled-in prompt
You are a careful Python engineer who writes tests that catch real regressions and survive refactoring.

<context>
The code under test:
<code>
def parse_duration(text: str) -> int:
    """Parse "1h30m", "45s" or "2h" into seconds."""
    units = {"h": 3600, "m": 60, "s": 1}
    total, number = 0, ""
    for ch in text.strip():
        if ch.isdigit():
            number += ch
        elif ch in units and number:
            total += int(number) * units[ch]
            number = ""
        else:
            raise ValueError(f"bad duration: {text!r}")
    return total
</code>

An existing test in this project, to match its style and helpers:
<style_example>
def test_slugify_lowercases_and_joins_words():
    assert slugify("Hello World") == "hello-world"
</style_example>
</context>

<constraints>
- Test behaviour through the public interface, not private helpers or the order of internal calls.
- Cover: the normal case, boundaries (empty, zero, one, maximum), invalid input and each error the code can raise or return.
- Mock only what crosses a process boundary, such as the network, the clock or the file system. Use real objects otherwise.
- Give each test a name that states the expected behaviour.
- If the code has a bug that a test exposes, keep the test, mark it clearly, and explain the bug after the code. Never weaken a test to make it pass.
</constraints>

<output_format>
One code block with the complete test file, ready to run. After it, a short list of any behaviour you couldn’t test without changing the code, and why.
</output_format>

<task>
Write unit tests for the code above using pytest, with pytest.mark.parametrize for tables of cases. Cover the behaviour a caller relies on, including edge cases and failure paths.
</task>

Models

Tips by model family

  • Claude

    In an agent loop, Anthropic suggests telling Claude not to hard-code values or special-case test inputs, and to say if a test is wrong rather than working around it. Add that line if the model will also edit the code under test.

  • OpenAI GPT

    If the first output misses the style, add the existing test as a worked example rather than more rules. OpenAI suggests reasoning models try zero-shot first and add examples only when the output misses.

  • All models

    Run the tests. The model can’t execute code in a plain chat, so a test that looks right may fail on an import or a fixture name; an agent with a terminal can run and fix them in one pass.

Related

Building this into an app that sends the same instructions with every request? Start from a system prompt instead; each comes with design notes, failure modes and a monthly cost. In the system prompt library: Unit test writer.

Edited the template? Compare your version with this one in the prompt diff, and count it for your model with the token counter. Browse all 29 prompt templates.