pytest parametrization with AI: test coupon thresholds, stacking and rounding

Build fourteen runnable cases from coupon rules, using pytest parametrization and exception assertions. Introduce a deliberate threshold bug to check whether AI-assisted tests actually detect it.

A coupon promises 20 off an order of 100 or more, but an order worth exactly 100 gets no discount. Imagine asking AI to add tests and receiving ten passing cases. On inspection, the amounts are 50, 120 and 200: none is exactly 100. There are plenty of tests, but the boundary rule is missing.

This tutorial uses a fictional checkout function to demonstrate pytest parametrization. It is for developers and testers who can run Python and want to test small tools or business functions. You get complete code, fourteen cases, prompts for AI assistance and a deliberately introduced bug to verify that the tests detect it. The example neither connects to payments nor reads real orders.

Define the rules before calculating expected results

Amounts are integers in cents. Members receive 10% off, rounded half up to the nearest cent. Coupon eligibility uses the original subtotal: at 10,000 cents or above, deduct 2,000 cents after the member discount. Negative amounts raise ValueError; non-integers raise TypeError. Pass only booleans for member and coupon flags. Shipping, tax, multiple coupons and order state are outside this example.

A member order of 100 with a coupon therefore costs 70. A member order of 99.99 does not qualify for the coupon and costs 89.99. Do not ask AI to derive expected values from the function’s current outputs. Expectations must come from the rules and independent calculations if the tests are to catch implementation mistakes.

pytest is useful for expressing multiple inputs as a parameter table and asserting exceptions. Running these tests requires no paid AI service or model API. If you already have Claude, use it to review gaps in the rules; Cursor can help create project files, while GitHub Copilot can assist with editing. AI feature access depends on your account. Use what you have, and verify correctness by running tests.

Put two files in a small directory

Use a Python environment supported by the pytest version you install. Create a coupon-demo directory and a virtual environment inside it. In Windows PowerShell:

py -m venv .venv
.\.venv\Scripts\python.exe -m pip install pytest

On macOS or Linux, use python3 -m venv .venv, followed by .venv/bin/python -m pip install pytest. Calling the environment’s interpreter directly avoids activation scripts and unnecessary changes to PowerShell execution policy. Use the corresponding interpreter path in the test commands below.

Save this as pricing.py. Adding 5 before integer division implements half-up rounding for the non-negative cent amounts in this example. It is not a universal rounding policy for all currencies or businesses.

def payable_cents(subtotal_cents, member=False, coupon=False):
    if type(subtotal_cents) is not int:
        raise TypeError("subtotal_cents must be an integer")
    if subtotal_cents < 0:
        raise ValueError("subtotal_cents must not be negative")

    amount = (subtotal_cents * 9 + 5) // 10 if member else subtotal_cents
    if coupon and subtotal_cents >= 10000:
        amount -= 2000
    return amount

Ask AI for cases before asking for assertions

Give AI the rules before the implementation and ask for inputs and expected amounts. Test one cent below the threshold, exactly at it and one cent above; combine membership and coupons, use small amounts for rounding and handle invalid inputs separately. Recalculate the AI’s expectations before approving a test file.

Design a case table for a Python checkout function using only these rules: integer cent amounts; members pay 90%, rounded half up to the nearest cent; a coupon uses the original subtotal, qualifies at 10000 cents and deducts 2000 cents after the member discount. Negative amounts raise ValueError. Non-integers, including True, raise TypeError. Flags are booleans only. Cover 9999, 10000 and 10001 cents, member/coupon combinations, rounding at 105 and 115 cents, zero and invalid amounts. For each row, give inputs, expected output, the rule checked and the calculation. Do not write code yet. Ask about unspecified behavior instead of inventing it.

After reviewing the table, ask for pytest tests using parametrize and clear ids, with raises for exceptions. Explicitly prohibit changing pricing.py, calling the tested function to calculate expected values, swallowing exceptions or marking failures as skipped. Include the final rules and approved table with that request rather than relying on earlier context.

One parameter table runs fourteen independent cases

Save the following as test_pricing.py beside pricing.py. The first table contains ten valid cases. Three invalid types and one negative amount add four exception cases. pytest expands parameter sets and identifies failing cases with names such as exact or stack.

import pytest

from pricing import payable_cents


@pytest.mark.parametrize(
    "subtotal,member,coupon,expected",
    [
        (0, False, False, 0),
        (9999, False, True, 9999),
        (10000, False, True, 8000),
        (10001, False, True, 8001),
        (10000, True, False, 9000),
        (10000, True, True, 7000),
        (9999, True, True, 8999),
        (105, True, False, 95),
        (115, True, False, 104),
        (10000, False, False, 10000),
    ],
    ids=["zero", "below", "exact", "above", "member", "stack", "member-below", "round-105", "round-115", "plain"],
)
def test_payable(subtotal, member, coupon, expected):
    assert payable_cents(subtotal, member, coupon) == expected


@pytest.mark.parametrize("bad", [True, 100.0, "10000"])
def test_reject_non_integer(bad):
    with pytest.raises(TypeError):
        payable_cents(bad)


def test_reject_negative():
    with pytest.raises(ValueError):
        payable_cents(-1)

Run from coupon-demo: .\.venv\Scripts\python.exe -m pytest -q on Windows, or .venv/bin/python -m pytest -q on macOS/Linux. With the files saved correctly, expect 14 passed. Timing depends on the environment.

Introduce a bug to check whether the green result means anything

In a copy of the example, temporarily change subtotal_cents >= 10000 to subtotal_cents > 10000, then rerun. AI Islands verified this in an isolated environment with pytest 9.1.1: 2 failed, 12 passed. The ordinary order at the threshold incorrectly returns 10000 instead of 8000, and the member/coupon combination returns 9000 instead of 7000.

The two cases therefore check that reaching the threshold qualifies. Restore >= and all fourteen pass again. This experiment confirms detection of one particular bug, not coverage of every possible defect. Do not perform it in production checkout code.

For an AI-assisted diagnosis, provide the rules, relevant function, failing inputs and assertion. Ask for an explanation before a minimal change. Do not let it replace the expected value with the actual output: that turns a test detecting a bug into a test accepting it.

Small details that can undermine the tests

  • Mixed units: 100 cents is 1, not the coupon threshold. Keep cents explicit in the interface, prompt and case table.
  • Booleans as amounts: Python’s bool inherits from int, so this example uses a strict type check and tests True. If a real product accepts Decimal or numeric strings, specify conversion separately.
  • Casual rounding: Calling round() is not an interchangeable implementation of the half-up rule here. Familiar code should not replace an agreed business rule.
  • Import errors: Keep the files together and run python -m pytest from that directory. More complex src layouts require proper project installation, not arbitrary path changes to hide the problem.
  • Green without the intended cases: Use filenames matching discovery conventions such as test_*.py and check the collected count. A successful exit code is not the only acceptance criterion.

Before submitting, check all three threshold positions, confirm discount order with a human, independently calculate expectations, use specific exception types, verify that a deliberate bug fails, restore it and rerun. Keep the command and results. A real project also needs its own cancellation, repeat-checkout and other scenarios; this example is not a complete order-system test suite.

For syntax, consult pytest’s parametrization guide and exception assertion guide, plus the Python venv documentation. Keep rules, case tables, implementation and run results in one review context so AI can help inspect specific problems.

Independently prepared by AI Islands using official product pages and public sources. Features and pricing may change; check official sites for current information.