How to Write Test Cases for Coupon Codes (With Examples)

How to Write Test Cases for Coupon Codes (With Examples)

May 25, 2026 · 16 min read · How-To Guides

How to Write Test Cases for Coupon Codes (With Examples)

Coupon codes are a common feature in e‑commerce, SaaS, and mobile apps. They drive promotions, acquire new users, and encourage repeat purchases. Because they touch pricing, inventory, and user experience, a defect in coupon handling can lead to revenue loss, compliance issues, or frustrated customers. This guide shows you how to design test cases that uncover those risks, from simple positive checks to complex edge conditions that only appear in production. After reading, you will have a concrete test matrix, a clear process for prioritizing and maintaining cases, and practical tips for coupling manual design with autonomous exploration.

1. Understanding Coupon Code Functionality

Before writing any test, you need a shared mental model of what the coupon system does. Most implementations share a core set of concepts, though details vary by platform.

1.1 Core Entities and Attributes

EntityTypical AttributesDescription
Couponcode, type (percent, fixed amount, free shipping), value, start_date, end_date, usage_limit, per_user_limit, applicable_products, excluded_products, minimum_order_value, stackable (yes/no)The promotion rule that the engine evaluates at checkout.
Userid, tier, coupon_usage_historyDetermines eligibility based on past usage or segment.
Orderitems, subtotal, tax, shipping, applied_coupons, final_totalThe transaction where the coupon is applied.
Coupon Redemption Logcoupon_id, user_id, order_id, timestamp, status (success, failed_reason)Audit trail for debugging and fraud detection.

1.2 Typical Flow

  1. User enters a coupon code in the cart or checkout page.
  2. Front‑end sends the code to a validation endpoint (often /api/coupons/validate).
  3. Back‑end checks existence, date validity, usage limits, product applicability, and minimum order value.
  4. If valid, the engine calculates discount, updates order totals, and returns the new amount.
  5. On order placement, the system records a redemption and decrements any usage counters.
  6. If invalid, the system returns an error code (e.g., COUPON_EXPIRED, INVALID_CODE, MIN_ORDER_NOT_MET).

Understanding these steps lets you map each test case to a specific validation or calculation point.

2. Test Case Anatomy

A well‑structured test case makes review, execution, and automation easier. Use a consistent template so that anyone on the team can grasp intent quickly.

2.1 Mandatory Fields

2.2 Optional but Helpful Fields

2.3 Example Template (Markdown)


**Test ID:** CPN-001  
**Title:** Valid percent‑off coupon reduces order total correctly  
**Preconditions:**  
- User authenticated  
- Cart contains two items: $50 + $80 = $130 subtotal  
- Coupon “SPRING10” exists: 10% off, no min order, valid today  

**Steps:**  
1. Navigate to cart page.  
2. Enter coupon code `SPRING10` in the input field.  
3. Click “Apply”.  
4. Observe updated order summary.  

**Expected Result:**  
- Discount line shows $13.00 (10% of $130).  
- New total = $117.00.  
- No error message displayed.  

**Postconditions:**  
- Clear cart or remove coupon to avoid side effects in subsequent tests.  

**Tags:** positive, api, ui  
**Linked Requirement:** US-1123 – Apply percent‑off coupon

Using this structure consistently will make it trivial to export cases to test management tools or generate automated skeletons.

3. Positive Test Cases

Positive cases verify that the coupon works as intended when all conditions are satisfied. They form the baseline confidence that the happy path is solid.

3.1 Simple Percent‑Off

IDPreconditionsStepsExpected Result
CPN-001User logged in, cart $130 subtotal, coupon SPRING10 (10% off, no min)1. Open cart 2. Enter SPRING10 3. Click ApplyDiscount $13, new total $117, no error
CPN-002Guest user, cart $50, coupon WELCOME5 (5$ off, min $0)1. Enter code 2. ApplyDiscount $5, new total $45
CPN-003User with loyalty tier “Gold”, cart $200, coupon GOLD20 (20% off, max usage 1 per user)1. Apply coupon 2. Place orderDiscount $40, order total $160, redemption log shows success, coupon usage count = 1

3.2 Fixed‑Amount and Free Shipping

IDPreconditionsStepsExpected Result
CPN-004Cart $75, coupon SHIPFREE (free shipping, min $50)1. Apply codeShipping charge $0, total = $75
CPN-005Cart $30, coupon FLAT10 (flat $10 off, min $20)1. Apply codeDiscount $10, new total $20
CPN-006Cart $120, coupon BUNDLE (free item SKU‑X if cart ≥ $100)1. Apply codeSKU‑X added with $0 price, total unchanged ($120)

3.3 Stackable Coupons (if allowed)

IDPreconditionsStepsExpected Result
CPN-007Cart $200, coupon A TENOFF (10% off), coupon B FIVESHIP ($5 shipping off), both stackable1. Apply TENOFF 2. Apply FIVESHIPDiscount $20 from A, shipping $0 from B, total $180
CPN-008Same as CPN-007 but coupon B marked non‑stackable1. Apply TENOFF 2. Apply FIVESHIPOnly TENOFF applied; system shows warning “Coupon cannot be combined” and ignores second

3.4 Date‑Bound Coupons

IDPreconditionsStepsExpected Result
CPN-009Today = 2025-11-02, coupon BLACKFRIDAY valid 2025-11-01 to 2025-11-03, 15% off1. Apply codeDiscount applied correctly
CPN-010Today = 2025-11-04 (outside validity), same coupon1. Apply codeError COUPON_EXPIRED shown, no discount

3.5 Usage Limits

IDPreconditionsStepsExpected Result
CPN-011Coupon LIMIT5 (global limit 5 uses), currently 4 uses1. User A applies couponDiscount applied, usage count becomes 5
CPN-012Same coupon, now at limit1. User B attempts to applyError COUPON_USAGE_LIMIT_EXCEEDED
CPN-013Per‑user limit 2, user has used 11. User applies coupon second timeDiscount applied, usage count for user = 2
CPN-014Same user attempts third use1. Apply couponError PER_USER_LIMIT_EXCEEDED

These positive cases give you confidence that the core discount logic, date checks, and limit enforcement work.

4. Negative and Invalid Input Cases

Negative testing ensures the system gracefully rejects malformed or unauthorized attempts. It also surfaces security gaps such as coupon guessing or tampering.

4.1 Code Format and Existence

IDPreconditionsStepsExpected Result
CPN-015Cart $501. Enter blank code 2. ApplyError COUPON_CODE_REQUIRED
CPN-016Cart $501. Enter !!@@## 2. ApplyError INVALID_COUPON_FORMAT (only alphanumerics and hyphens allowed)
CPN-017Cart $501. Enter a 32‑character random string 2. ApplyError COUPON_NOT_FOUND
CPN-018Cart $501. Enter valid code but with leading/trailing spaces 2. ApplySystem trims and accepts or rejects with INVALID_COUPON_FORMAT – specify which behavior is required

4.2 Business Rule Violations

IDPreconditionsStepsExpected Result
CPN-019Cart $10, coupon MIN50 (requires $50 min)1. Apply codeError MIN_ORDER_NOT_MET
CPN-020Cart $100, coupon EXCLUDED (excludes SKU‑Y) and cart contains SKU‑Y1. Apply codeError PRODUCT_EXCLUDED
CPN-021Cart $100, coupon NEWUSER (only for users with zero prior orders) and user has placed one order before1. Apply codeError INELIGIBLE_USER
CPN-022Cart $100, coupon FIRSTTIME (valid only for first purchase) and user already used it1. Apply codeError COUPON_ALREADY_REDEEMED_BY_USER

4.3 Tampering and Replay

IDPreconditionsStepsExpected Result
CPN-023Valid coupon SAVE10 intercepted via proxy1. Modify request to change value from 10% to 90% 2. Send to /validateServer rejects with INVALID_SIGNATURE or recalculates based on stored coupon definition
CPN-024Coupon used in order #10011. Capture redemption request 2. Replay same request with different order IDServer rejects with COUPON_ALREADY_REDEEMED (usage count prevents reuse)
CPN-025Coupon with future start date1. Attempt to use before start dateError COUPON_NOT_YET_VALID
CPN-026Coupon with past end date1. Attempt to use after expiryError COUPON_EXPIRED

4.4 Rate Limiting and Abuse

IDPreconditionsStepsExpected Result
CPN-027Anonymous user1. Rapidly send 100 validation requests with random codesServer responds with HTTP 429 after threshold, or temporarily blocks IP
CPN-028Authenticated user1. Try to apply same coupon 50 times in quick succession on same cartAfter first success, subsequent attempts return COUPON_ALREADY_APPLIED_TO_CART or rate‑limit error

These cases protect against both functional bugs and potential fraud vectors.

5. Boundary and Edge Cases

Boundary testing focuses on limits of numeric fields, date handling, and string lengths. Edge cases combine multiple constraints to reveal hidden interactions.

5.1 Numeric Limits

IDPreconditionsStepsExpected Result
CPN-029Coupon PERCENT100 (100% off)1. Apply to cart $75Discount $75, total $0 (free order)
CPN-030Coupon PERCENT101 (101% off) – invalid per spec1. Attempt to create coupon via admin APIValidation error PERCENT_MUST_BE_0_100
CPN-031Coupon FLAT0 (0$ off)1. Apply to cart $50Discount $0, total unchanged, no error
CPN-032Coupon FLAT-5 (negative amount) – invalid1. Attempt creationError AMOUNT_MUST_BE_POSITIVE
CPN-033Minimum order value = $0.011. Cart $0.009 (due to rounding) 2. Apply coupon with min $0.01Error MIN_ORDER_NOT_MET (due to precision)
CPN-034Minimum order value = $1000, cart $999.99991. Apply couponError MIN_ORDER_NOT_MET (floating‑point handling)

5.2 Date and Time Edge Cases


| Preconditions | Steps | Expected Result |
|----|--------------------|-------|-----------------|
| CPN-035 | System clock set to 2025-04-15 12:00:00 UTC, coupon valid 2025-04-15 00:00:00 – 2025-04-15 23:59:59 | 1. Apply at 12:00:00 | Discount applied |
| CPN-036 | Same coupon, system clock set to 2025-04-15 23:59:59 | 1. Apply at 23:59:59 | Discount applied |
| CPN-037 | System clock set to 2025-04-16 00:00:00 (one second after end) | 1. Apply | Error `COUPON_EXPIRED` |
| CPN-038 | Coupon uses only date (no time) field, start = 2025-05-01, end = 2025-05-03 | 1. Apply at 2025-05-02 23:59:59 (local timezone) | Depending on implementation, either valid or invalid – test both UTC and local handling |
IDPreconditionsStepsExpected Result
CPN-035

5.3 String Length and Character Set

IDPreconditionsStepsExpected Result
CPN-039Admin UI allows coupon code up to 20 characters1. Create code with 20 chars ABCDEFGHIJKLMNOPQRSTAccepted
CPN-040Same limit, try 21 characters1. Create code with 21 charsError CODE_TOO_LONG
CPN-041Code must match regex [A-Z0-9-]+1. Try lowercase abc123Error INVALID_CHARACTER (if case‑sensitive)
CPN-042Code contains Unicode (e.g., SPRİNG10 with dotted I)1. ApplyError INVALID_CHARACTER or normalized depending on backend
CPN-043Leading/trailing spaces trimmed automatically1. Enter " SPRING10 "Accepted as SPRING10 (if trimming implemented) or rejected – specify expected behavior

5.4 Combined Boundary Scenarios

IDPreconditionsStepsExpected Result
CPN-044Coupon EDGE: 99% off, max usage 1, valid today, min order $0.011. Cart $0.009 (below min) 2. ApplyError MIN_ORDER_NOT_MET (even though discount would make total negative)
CPN-045Same coupon, cart $0.011. ApplyDiscount $0.0099 (rounded to $0.01?), total $0.00 – verify rounding policy
CPN-046Coupon ZEROVALUE: 0% off, per‑user limit 5, valid for 1 year1. Apply 5 times in same dayEach application succeeds, no discount, usage counter increments
CPN-047Same coupon, try 6th use1. ApplyError PER_USER_LIMIT_EXCEEDED

These edge cases often surface defects in rounding, timezone conversion, or incorrect logical operators (using > instead of >=).

6. Business Rule and Promotion Logic Cases

Beyond simple arithmetic, coupons frequently interact with complex promotional engines: tiered discounts, buy‑X‑get‑Y, referral bonuses, and dynamic pricing.

6.1 Tiered Discounts (Spend‑Based)

IDPreconditionsStepsExpected Result
CPN-048Coupon SPENDTIER: 5% off if subtotal ≥ $100, 10% off if ≥ $200, 15% off if ≥ $5001. Cart $150 2. ApplyDiscount $7.50 (5% of $150)
CPN-049Same coupon, cart $2501. ApplyDiscount $25.00 (10%)
CPN-050Same coupon, cart $6001. ApplyDiscount $90.00 (15%)
CPN-051Same coupon, cart $991. ApplyError MIN_ORDER_NOT_MET (tier not reached)

6.2 Buy‑X‑Get‑Y (BXGY)

IDPreconditionsStepsExpected Result
CPN-052Coupon BOGO50: Buy one SKU‑A, get second 50% off (same SKU)1. Add two SKU‑A ($40 each) to cartSubtotal $80
CPN-053Same scenario1. Apply couponDiscount $20 (50% of second item), new total $60
CPN-054Cart contains three SKU‑A ($40 each)1. Apply couponDiscount applies to only one extra item (second), third stays full price → total $100
CPN-055Cart contains SKU‑A and SKU‑B ($30)1. Apply couponNo discount because second item not SKU‑A; error BOGO_ELIGIBLE_ITEM_MISSING or no change depending on spec

6.3 Referral and Affiliate Codes

IDPreconditionsStepsExpected Result
CPN-056Referral code REF123 gives $10 to referrer and $5 to referee on first purchase ≥ $301. New user signs up via referral link, cart $401. Apply REF123 → discount $5 for new user, referral log shows $10 credit to referrer
CPN-057Existing user tries to use own referral code1. Apply REF123Error SELF_REFERRAL_NOT_ALLOWED
CPN-058Referee cart $20 (below min)1. Apply codeError MIN_ORDER_NOT_MET (referral still not applied)
CPN-059Referrer already reached monthly reward cap ($100)1. New user completes qualifying purchaseReferrer receives no additional credit; system logs CAP_REACHED

6.4 Dynamic Pricing and Personalized Offers

IDPreconditionsStepsExpected Result
CPN-060User receives personalized coupon PERSONAL20 (20% off) based on past purchase category1. Log in as targeted user, cart $100 of matching category1. Apply coupon → discount $20
CPN-061Same user, cart contains items outside targeted category1. Apply couponError INELIGIBLE_PRODUCTS
CPN-062Coupon is single‑use but generated per user; after use, code should be invalid for same user1. Apply coupon, complete order 2. Try to apply same code againError COUPON_ALREADY_REDEEMED_BY_USER
CPN-063Coupon is time‑limited to 24h after generation1. Generate coupon at 10:00, wait 25h, attempt useError COUPON_EXPIRED (based on issuance timestamp)

These cases ensure that the promotion engine correctly evaluates eligibility rules, applies the right calculation, and updates any associated counters or rewards.

7. Integration and Flow Test Cases

Coupon behavior does not exist in isolation; it must work across the entire checkout flow, payment gateways, inventory reservation, and order fulfillment.

7.1 End‑to‑End Checkout with Coupon

IDPreconditionsStepsExpected Result
CPN-064User logged in, cart $120, coupon SAVE15 (15% off)1. Apply coupon → see discount $18 2. Proceed to payment 3. Enter valid credit card 4. Submit orderOrder confirmation page shows final total $102, order details list coupon code and discount amount, payment gateway receives $102, inventory is decremented, redemption log created
CPN-065Same as CPN-064 but coupon invalid (expired)1. Attempt to apply coupon → error shown 2. Proceed to paymentUser cannot proceed to payment until coupon removed or replaced with valid one; order total stays $120
CPN-066Cart contains a non‑refundable gift card ($30) and coupon SHIPFREE (free shipping, min $50)1. Apply coupon → shipping $0 2. Checkout with gift card + credit card for remaining $70Gift card balance reduced by $30, credit card charged $70, shipping $0, order total $100
CPN-067Coupon applies to subscription first‑month discount; user selects monthly plan1. Apply coupon → first month price $0 (100% off) 2. Complete sign‑upSubscription created, first invoice $0, subsequent invoices at full price, redemption log linked to subscription ID

7.2 Payment Gateway Interaction

IDPreconditionsStepsExpected Result
CPN-068Coupon reduces order to $0.00 (free order)1. Apply coupon → total $0 2. Proceed to paymentPayment screen shows “No payment required” or skips payment step entirely; order completes with zero‑dollar transaction
CPN-069Coupon makes order total negative (due to bug)1. Apply coupon → total $-5 2. Attempt paymentSystem blocks checkout with error INVALID_ORDER_TOTAL (must be ≥ 0)
CPN-070Coupon applies after tax calculation (tax on discounted amount)1. Cart $100, tax 10% → $110 2. Coupon TAXEXEMPT (tax exempt, not discount)Tax $0, total $100 (coupon removes tax only)
CPN-071Coupon applies before tax (tax on pre‑discount amount)1. Cart $100, tax 10% 2. Coupon PRETAX10 (10% off)Discount $10, subtotal $90, tax $9, total $99

7.3 Inventory and Reservation

IDPreconditionsStepsExpected Result
CPN-072Limited‑stock item (5 units) with coupon BUY2GET1 (buy 2, get 1 free)1. Add 6 units to cart (intend to get 2 free) 2. Apply couponSystem calculates: pay for 4 units, get 2 free → reserves 6 units, inventory decremented by 6 upon order
CPN-073Same scenario but only 4 units in stock1. Add 6 units → stock insufficient error before coupon applicationSystem prevents adding beyond stock; coupon not applied
CPN-074Coupon provides free item SKU‑Z that is out of stock1. Cart meets conditions for free SKU‑Z 2. Apply couponError FREE_ITEM_OUT_OF_STOCK; order cannot be completed until alternative or removal of coupon

These flow tests verify that coupon logic integrates cleanly with downstream systems and does not leave the system in an inconsistent state.

8. Automation Strategies and Tooling

Manual test case design is essential, but executing hundreds of variations by hand is inefficient. Combining well‑written cases with automated scripts and autonomous exploration yields both depth and breadth.

8.1 Choosing the Right Automation Layer

LayerWhen to UseTools (examples)
UnitValidate pure calculation functions (discount, eligibility)JUnit, pytest, Jest
APITest validation endpoint directly, fast and deterministicPostman/Newman, Rest-Assured, karate
UI (Web)Verify end‑to‑end flow, coupon input UI, error messagesPlaywright, Cypress, Selenium
UI (Mobile)Same for native apps, handling soft keyboard, toastAppium, Espresso, XCUITest
ContractEnsure coupon service API schema stays stablePact, Dredd
PerformanceCheck system under load with many coupon validationsk6, Gatling, Locust

A typical strategy: unit tests for the discount algorithm, API tests for validation logic, and a subset of UI tests for the happy‑path and a few error flows. The rest can be covered by autonomous exploration.

8.2 Example: Parameterized API Test in pytest


import pytest
import requests

BASE_URL = "https://api.example.com/coupons"

@pytest.mark.parametrize(
    "code, cart_subtotal, expected_discount, expected_error",
    [
        ("SPRING10", 130.0, 13.0, None),
        ("FLAT10", 30.0, 10.0, None),
        ("MIN50", 40.0, None, "MIN_ORDER_NOT_MET"),
        ("EXPIRED", 100.0, None, "COUPON_EXPIRED"),
        ("", 50.0, None, "COUPON_CODE_REQUIRED"),
        ("!!@@", 20.0, None, "INVALID_COUPON_FORMAT"),
    ],
)
def test_coupon_validation(code, cart_subtotal, expected_discount, expected_error):
    payload = {"code": code, "cart_subtotal": cart_subtotal}
    resp = requests.post(f"{BASE_URL}/validate", json=payload, timeout=5)
    assert resp.status_code == 200
    data = resp.json()
    if expected_error:
        assert data["error"] == expected_error
        assert "discount" not in data
    else:
        assert data["discount"] == pytest.approx(expected_discount, rel=1e-9)
        assert data["new_total"] == pytest.approx(cart_subtotal - expected_discount)

This test covers positive, negative, boundary, and format cases in a few lines. Adding more parametrized rows expands coverage quickly.

8.3 Example: Playwright Script for UI Flow


const { test, expect } = require('@playwright/test');

test('apply coupon reduces total and proceeds to checkout', async ({ page }) => {
  await page.goto('https://shop.example.com/cart');
  await page.fill('[data-test="cart-subtotal"]', '130'); // mock or set via API
  await page.fill('[data-test="coupon-input"]', 'SPRING10');
  await page.click('[data-test="apply-btn"]');

  const discount = await page.textContent('[data-test="discount-amount"]');
  expect(discount).toBe('$13.00');

  const total = await page.textContent('[data-test="order-total"]');
  expect(total).toBe('$117.00');

  await page.click('[data-test="checkout-btn"]');
  await expect(page).toHaveURL(/.*\/payment/);
  const paymentAmount = await page.locator('[data-test="payment-amount"]').innerText();
  expect(paymentAmount).toBe('$102.00'); // assuming tax included elsewhere
});

Such a script can be run in CI on every pull request, giving fast feedback on the coupon UI.

8.4 Leveraging Autonomous Exploration with SUSA

SUSA (SUSATest) can complement the above automated suite by discovering scenarios that were not anticipated in the test plan. After uploading an APK or pointing SUSA at a web URL, the agent:

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free