Should I test that?

Should I test prompts?

Verdict

Test it differently

Test a prompt differently: score every prompt or model change against an evaluation set of real inputs, and read a sample of production replies, instead of asserting exact reply text in unit tests.

Why

Test a prompt differently: run an evaluation set of real inputs on every prompt or model change, and read a sample of production replies. For a prompt behind a product feature, Blast radius is users and Change frequency is regularly, because customers read the replies and the team rewords the prompt about once a month. Detectability is eventually, because a worse reply is still fluent text and raises no error. Reversibility is with-effort: a revert takes one deploy, but stored replies need repair. Test cost is heavy, because the reply differs on every run, so a useful check needs graded real inputs, and rule R9 gives Test it differently.

When the decision changes
WhenDecisionWhy
You run the prompt by hand in a chat window for your own workDo not test the prompt; read each reply before you use itBlast radius falls to none: a bad reply reaches only you
The prompt's reply quotes prices or grants refundsTest mandatory: evaluation cases for every pricing and refund rule gate each releaseBlast radius rises to money and Reversibility to costly
The prompt answers questions from customer recordsTest mandatory: evaluation cases that try to pull another customer's data run on every changeBlast radius rises to safety-or-legal and Detectability to never: a leaked record reads like a normal answer
The prompt sorts support tickets into a fixed list of queuesTest: assert the expected queue for labelled examples in CI, with a minimum pass rateTest cost falls to moderate: the output is a label from a fixed list, which a test compares exactly; Detectability stays eventually
The reply goes to customers as an email that no person reads firstTest: run the evaluation set in CI, and block the release on a failed caseReversibility rises to impossible: a sent email cannot be recalled
The prompt runs once to tag an archive of old documentsTest it differently: rehearse on a sample of 200 documents and check the tags by handChange frequency moves to once: an evaluation set would never run again

What breaks if you don't test

A prompt edit that fixes one case can quietly break others. The model follows the new wording, drops a detail that the old wording asked for, and every reply still reads well. A new model version does the same without any edit on your side. Agents or customers notice the missing detail weeks later.

What you lose if you over-test

Unit tests that assert the exact text of a reply break with the next model version, and at any temperature above zero they fail on some runs. The team reruns them until they pass, then stops reading them. An evaluation set on every commit costs time and API calls: 300 cases at 2 seconds per call add 10 minutes to a sequential CI run.

What to do instead

  1. Collect 50 real inputs from your logs, plus every input that produced a bad reply before.
  2. For each input, write checks: exact match for labels and fields, a "contains" check for facts such as an order number, and a rubric that a person or a second model scores.
  3. Run the set on every change to the prompt, the template or the model version. A case that passed before and fails now is a regression.
  4. Validate structured replies against a JSON schema in code, as Gabriel Anhaia recommends, and unit test the code that fills the template.
  5. Log the prompt version with each production reply, and read a weekly sample.

Anthropic's guide to building evaluations describes grading methods. If users type text into the prompt, add the injection cases from OWASP LLM01.

When the answer changes

  • The reply quotes prices, grants refunds, or reads personal data: the evaluation set becomes mandatory.
  • Replies reach customers before any person reads them: the set becomes a CI gate.
  • The prompt returns one label from a fixed list: exact assertions work as ordinary tests.

Real incident + Code example

The summary that lost the order number

On a support tool I worked on, the model summarised each new ticket for the agent. A colleague shortened the prompt to get briefer summaries, checked five tickets by hand, and shipped. The shorter prompt no longer asked for the order number, and the model began to leave it out of long tickets. Agents looked up orders by hand for two weeks before anyone traced the problem to the edit. One check over real tickets would have failed on the first run:

# load_cases and summarize are the project's own functions.
CASES = load_cases("evals/ticket_summary.jsonl")  # real tickets from logs

def check(case, summary):
    problems = []
    for order_id in case["order_ids"]:
        if order_id not in summary:
            problems.append(f"missing order {order_id}")
    if len(summary.split()) > 80:
        problems.append("longer than 80 words")
    return problems

def run(prompt_version):
    failed = {}
    for case in CASES:
        summary = summarize(case["ticket"], prompt_version)
        if problems := check(case, summary):
            failed[case["id"]] = problems
    return failed  # compare with the previous version's failures

The check accepts any wording and fails only on a missing fact or an overlong summary.

FAQ

Should you unit test your prompts?

No, do not unit test prompt replies with exact assertions; score them against an evaluation set of real inputs instead. Unit test the code around the prompt: the template, the schema check and the parser.

Should I test a prompt again when the model version changes?

Yes, run the full evaluation set before you move a prompt to a new model version, because a new version can follow the same wording differently. Pin the model version in configuration so that a switch happens only when you choose it (Should I retest my app after an LLM model upgrade?).

Can a unit test check the output of a language model?

Yes, a unit test can check structured model output: it validates a JSON reply against a schema or compares a label from a fixed list. For free text, an exact expected string fails on the next run, so check required facts in an evaluation set instead.