Promptabide Logo

Write characterization tests for untested code before a refactor

Pin down what legacy code actually does with a partition table, SUSPECT flags for odd behavior, and a runnable test file, before anyone changes it.

At a glance

Best for
Developers inheriting untested functions who need a safety net before refactoring or fixing them.
Tested on
Claude · Opus 5.5
You fill in
codeframework
You get
1–3. Partitions (traced by hand) Partition Input (subtotal, kg, country, express) Output Free-shipping threshold, exact 999, 1, "IN" 0 Just below… (full result below)

Prompt

Write tests for this untested code before anyone refactors it. The goal is to pin down its CURRENT behavior, including behavior that looks wrong.

Code:
{{code}}

Test framework: {{framework}}

Steps:
  • 1. List the input partitions as a table: partition | example input | output according to the code as written. Cover normal cases, each boundary (the value on each side of every comparison), empty/None values, and error paths.

  • 1. Trace each row through the code yourself. Don't guess outputs.

  • 1. Mark any row where the current behavior looks like a bug with SUSPECT and one sentence saying why. Do not fix it in the test. Assert what the code actually does, and leave a comment so a human can decide.

  • 1. Write the test file. One behavior per test, names that read as sentences, no mocks unless the code does I/O.

  • 1. Say which branches or conditions are still uncovered, if any.
  • Fill in

    {{code}}
    The function or class to test, pasted in fulle.g. shipping_fee() from checkout/fees.py
    {{framework}}
    Test framework and any conventionse.g. pytest, plain asserts, parametrize where it helps

    Example input

    framework: pytest, plain asserts, use parametrize where it keeps things short
    code:
    ```python
    # checkout/fees.py
    def shipping_fee(subtotal, weight_kg, country, express=False):
        if subtotal >= 999:
            base = 0
        elif country == "IN":
            base = 49
        else:
            base = 499
        if weight_kg > 5:
            base += int(weight_kg - 5) * 20
        if express:
            base = base * 2 if base else 99
        return base
    ```
    9790

    Generated Outputs (1)

    3 hours ago
    Claude
    Opus 5.5
    Generated Output
    1–3. Partitions (traced by hand)

    | Partition | Input (subtotal, kg, country, express) | Output |
    |---|---|---|
    | Free-shipping threshold, exact | 999, 1, "IN" | 0 |
    | Just below threshold | 998.99, 1, "IN" | 49 |
    | International, below threshold | 500, 1, "US" | 499 |
    | International, above threshold | 1500, 1, "US" | 0 SUSPECT: free international shipping may be meant for India only |
    | Lowercase country | 500, 1, "in" | 499 SUSPECT: "in" is charged as international |
    | Weight exactly 5 | 500, 5, "IN" | 49 |
    | Weight 5.9 | 500, 5.9, "IN" | 49 SUSPECT: int() floors the extra 0.9 kg, so no surcharge |
    | Weight 7.5 | 500, 7.5, "IN" | 89 |
    | Free order, heavy | 1200, 8, "IN" | 60 |
    | Express, paid order | 500, 1, "IN", True | 98 |
    | Express, free order | 1200, 1, "IN", True | 99 SUSPECT: costs more than express on a paid order |
    | Express, free and heavy | 1200, 8, "IN", True | 120 |

    4. Test file
    `python

    tests/test_shipping_fee.py


    import pytest
    from checkout.fees import shipping_fee

    @pytest.mark.parametrize("subtotal, expected", [(999, 0), (998.99, 49)])
    def test_free_shipping_starts_at_999(subtotal, expected):
    assert shipping_fee(subtotal, 1, "IN") == expected

    def test_international_orders_below_threshold_pay_499():
    assert shipping_fee(500, 1, "US") == 499

    def test_international_orders_above_threshold_ship_free():
    # SUSPECT: free-shipping rule ignores country
    assert shipping_fee(1500, 1, "US") == 0

    def test_lowercase_country_is_treated_as_international():
    # SUSPECT: no normalization of country code
    assert shipping_fee(500, 1, "in") == 499

    @pytest.mark.parametrize("kg, expected", [(5, 49), (5.9, 49), (7.5, 89)])
    def test_weight_surcharge_is_20_per_whole_kg_over_5(kg, expected):
    # SUSPECT at 5.9: partial kilograms are floored
    assert shipping_fee(500, kg, "IN") == expected

    def test_free_orders_still_pay_weight_surcharge():
    assert shipping_fee(1200, 8, "IN") == 60

    def test_express_doubles_a_paid_fee():
    assert shipping_fee(500, 1, "IN", express=True) == 98

    def test_express_on_a_free_order_costs_flat_99():
    # SUSPECT: more than express on a paid domestic order (98)
    assert shipping_fee(1200, 1, "IN", express=True) == 99

    def test_express_doubles_the_surcharge_on_free_heavy_orders():
    assert shipping_fee(1200, 8, "IN", express=True) == 120
    `

    5. Coverage
    Every branch is exercised. Nothing covers invalid input: negative weights, None for country, or a string subtotal. The function has no validation, so decide whether that belongs here or at the caller before adding tests for it.

    Why it works

    Characterization tests only work if they record what the code does, not what it should do, and the prompt says so twice. Without that, models quietly "fix" bugs in their expected values and your safety net fails on day one. The partition table with boundaries on each side of every comparison is what surfaced the 5.9 kg flooring and the 98 vs 99 express oddity. "Trace each row yourself" cuts down on invented outputs. SUSPECT flags turn the test run into a bug list for a human to triage, without changing behavior mid-refactor.

    When not to use it

    It's a poor fit for code whose behavior depends on databases, clocks or network calls you can't paste. There, you need integration tests or recorded fixtures, not traced partitions. For very large functions the hand-tracing gets unreliable. Split the function first, or run the generated tests and correct the expected values from real results.
    Comments (0)
    No comments yet. Be the first to share your thoughts!
    Top Creators
    Follow PromptAbide

    New bides, prompt breakdowns and community picks, on whichever feed you already read.

    Trending Tags
    Loading...