How to Write Test Cases for Coupon Codes (With Examples)
How to Write Test Cases for Coupon Codes (With Examples)
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
| Entity | Typical Attributes | Description |
|---|---|---|
| Coupon | code, 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. |
| User | id, tier, coupon_usage_history | Determines eligibility based on past usage or segment. |
| Order | items, subtotal, tax, shipping, applied_coupons, final_total | The transaction where the coupon is applied. |
| Coupon Redemption Log | coupon_id, user_id, order_id, timestamp, status (success, failed_reason) | Audit trail for debugging and fraud detection. |
1.2 Typical Flow
- User enters a coupon code in the cart or checkout page.
- Front‑end sends the code to a validation endpoint (often
/api/coupons/validate). - Back‑end checks existence, date validity, usage limits, product applicability, and minimum order value.
- If valid, the engine calculates discount, updates order totals, and returns the new amount.
- On order placement, the system records a redemption and decrements any usage counters.
- 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
- Test ID – unique identifier (e.g.,
CPN-001). - Title – short, descriptive phrase.
- Preconditions – system state required before starting (e.g., “User is logged in, cart contains $120 worth of eligible items”).
- Steps – numbered actions the tester performs.
- Expected Result – observable outcome (UI message, API response, database change).
- Postconditions – any cleanup needed (e.g., “Remove applied coupon from cart”).
- Tags – for filtering (e.g.,
positive,boundary,security). - Linked Requirement – reference to the spec or user story (e.g.,
US-1123).
2.2 Optional but Helpful Fields
- Test Data – specific coupon code, amount, dates.
- Automation Hint – note if the case is suited for UI, API, or contract test.
- Risk Level – low/medium/high based on impact and likelihood.
- Estimated Effort – in minutes, useful for sprint planning.
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
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-001 | User logged in, cart $130 subtotal, coupon SPRING10 (10% off, no min) | 1. Open cart 2. Enter SPRING10 3. Click Apply | Discount $13, new total $117, no error |
| CPN-002 | Guest user, cart $50, coupon WELCOME5 (5$ off, min $0) | 1. Enter code 2. Apply | Discount $5, new total $45 |
| CPN-003 | User with loyalty tier “Gold”, cart $200, coupon GOLD20 (20% off, max usage 1 per user) | 1. Apply coupon 2. Place order | Discount $40, order total $160, redemption log shows success, coupon usage count = 1 |
3.2 Fixed‑Amount and Free Shipping
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-004 | Cart $75, coupon SHIPFREE (free shipping, min $50) | 1. Apply code | Shipping charge $0, total = $75 |
| CPN-005 | Cart $30, coupon FLAT10 (flat $10 off, min $20) | 1. Apply code | Discount $10, new total $20 |
| CPN-006 | Cart $120, coupon BUNDLE (free item SKU‑X if cart ≥ $100) | 1. Apply code | SKU‑X added with $0 price, total unchanged ($120) |
3.3 Stackable Coupons (if allowed)
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-007 | Cart $200, coupon A TENOFF (10% off), coupon B FIVESHIP ($5 shipping off), both stackable | 1. Apply TENOFF 2. Apply FIVESHIP | Discount $20 from A, shipping $0 from B, total $180 |
| CPN-008 | Same as CPN-007 but coupon B marked non‑stackable | 1. Apply TENOFF 2. Apply FIVESHIP | Only TENOFF applied; system shows warning “Coupon cannot be combined” and ignores second |
3.4 Date‑Bound Coupons
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-009 | Today = 2025-11-02, coupon BLACKFRIDAY valid 2025-11-01 to 2025-11-03, 15% off | 1. Apply code | Discount applied correctly |
| CPN-010 | Today = 2025-11-04 (outside validity), same coupon | 1. Apply code | Error COUPON_EXPIRED shown, no discount |
3.5 Usage Limits
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-011 | Coupon LIMIT5 (global limit 5 uses), currently 4 uses | 1. User A applies coupon | Discount applied, usage count becomes 5 |
| CPN-012 | Same coupon, now at limit | 1. User B attempts to apply | Error COUPON_USAGE_LIMIT_EXCEEDED |
| CPN-013 | Per‑user limit 2, user has used 1 | 1. User applies coupon second time | Discount applied, usage count for user = 2 |
| CPN-014 | Same user attempts third use | 1. Apply coupon | Error 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
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-015 | Cart $50 | 1. Enter blank code 2. Apply | Error COUPON_CODE_REQUIRED |
| CPN-016 | Cart $50 | 1. Enter !!@@## 2. Apply | Error INVALID_COUPON_FORMAT (only alphanumerics and hyphens allowed) |
| CPN-017 | Cart $50 | 1. Enter a 32‑character random string 2. Apply | Error COUPON_NOT_FOUND |
| CPN-018 | Cart $50 | 1. Enter valid code but with leading/trailing spaces 2. Apply | System trims and accepts or rejects with INVALID_COUPON_FORMAT – specify which behavior is required |
4.2 Business Rule Violations
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-019 | Cart $10, coupon MIN50 (requires $50 min) | 1. Apply code | Error MIN_ORDER_NOT_MET |
| CPN-020 | Cart $100, coupon EXCLUDED (excludes SKU‑Y) and cart contains SKU‑Y | 1. Apply code | Error PRODUCT_EXCLUDED |
| CPN-021 | Cart $100, coupon NEWUSER (only for users with zero prior orders) and user has placed one order before | 1. Apply code | Error INELIGIBLE_USER |
| CPN-022 | Cart $100, coupon FIRSTTIME (valid only for first purchase) and user already used it | 1. Apply code | Error COUPON_ALREADY_REDEEMED_BY_USER |
4.3 Tampering and Replay
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-023 | Valid coupon SAVE10 intercepted via proxy | 1. Modify request to change value from 10% to 90% 2. Send to /validate | Server rejects with INVALID_SIGNATURE or recalculates based on stored coupon definition |
| CPN-024 | Coupon used in order #1001 | 1. Capture redemption request 2. Replay same request with different order ID | Server rejects with COUPON_ALREADY_REDEEMED (usage count prevents reuse) |
| CPN-025 | Coupon with future start date | 1. Attempt to use before start date | Error COUPON_NOT_YET_VALID |
| CPN-026 | Coupon with past end date | 1. Attempt to use after expiry | Error COUPON_EXPIRED |
4.4 Rate Limiting and Abuse
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-027 | Anonymous user | 1. Rapidly send 100 validation requests with random codes | Server responds with HTTP 429 after threshold, or temporarily blocks IP |
| CPN-028 | Authenticated user | 1. Try to apply same coupon 50 times in quick succession on same cart | After 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
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-029 | Coupon PERCENT100 (100% off) | 1. Apply to cart $75 | Discount $75, total $0 (free order) |
| CPN-030 | Coupon PERCENT101 (101% off) – invalid per spec | 1. Attempt to create coupon via admin API | Validation error PERCENT_MUST_BE_0_100 |
| CPN-031 | Coupon FLAT0 (0$ off) | 1. Apply to cart $50 | Discount $0, total unchanged, no error |
| CPN-032 | Coupon FLAT-5 (negative amount) – invalid | 1. Attempt creation | Error AMOUNT_MUST_BE_POSITIVE |
| CPN-033 | Minimum order value = $0.01 | 1. Cart $0.009 (due to rounding) 2. Apply coupon with min $0.01 | Error MIN_ORDER_NOT_MET (due to precision) |
| CPN-034 | Minimum order value = $1000, cart $999.9999 | 1. Apply coupon | Error MIN_ORDER_NOT_MET (floating‑point handling) |
5.2 Date and Time Edge Cases
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-035 |
5.3 String Length and Character Set
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-039 | Admin UI allows coupon code up to 20 characters | 1. Create code with 20 chars ABCDEFGHIJKLMNOPQRST | Accepted |
| CPN-040 | Same limit, try 21 characters | 1. Create code with 21 chars | Error CODE_TOO_LONG |
| CPN-041 | Code must match regex [A-Z0-9-]+ | 1. Try lowercase abc123 | Error INVALID_CHARACTER (if case‑sensitive) |
| CPN-042 | Code contains Unicode (e.g., SPRİNG10 with dotted I) | 1. Apply | Error INVALID_CHARACTER or normalized depending on backend |
| CPN-043 | Leading/trailing spaces trimmed automatically | 1. Enter " SPRING10 " | Accepted as SPRING10 (if trimming implemented) or rejected – specify expected behavior |
5.4 Combined Boundary Scenarios
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-044 | Coupon EDGE: 99% off, max usage 1, valid today, min order $0.01 | 1. Cart $0.009 (below min) 2. Apply | Error MIN_ORDER_NOT_MET (even though discount would make total negative) |
| CPN-045 | Same coupon, cart $0.01 | 1. Apply | Discount $0.0099 (rounded to $0.01?), total $0.00 – verify rounding policy |
| CPN-046 | Coupon ZEROVALUE: 0% off, per‑user limit 5, valid for 1 year | 1. Apply 5 times in same day | Each application succeeds, no discount, usage counter increments |
| CPN-047 | Same coupon, try 6th use | 1. Apply | Error 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)
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-048 | Coupon SPENDTIER: 5% off if subtotal ≥ $100, 10% off if ≥ $200, 15% off if ≥ $500 | 1. Cart $150 2. Apply | Discount $7.50 (5% of $150) |
| CPN-049 | Same coupon, cart $250 | 1. Apply | Discount $25.00 (10%) |
| CPN-050 | Same coupon, cart $600 | 1. Apply | Discount $90.00 (15%) |
| CPN-051 | Same coupon, cart $99 | 1. Apply | Error MIN_ORDER_NOT_MET (tier not reached) |
6.2 Buy‑X‑Get‑Y (BXGY)
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-052 | Coupon BOGO50: Buy one SKU‑A, get second 50% off (same SKU) | 1. Add two SKU‑A ($40 each) to cart | Subtotal $80 |
| CPN-053 | Same scenario | 1. Apply coupon | Discount $20 (50% of second item), new total $60 |
| CPN-054 | Cart contains three SKU‑A ($40 each) | 1. Apply coupon | Discount applies to only one extra item (second), third stays full price → total $100 |
| CPN-055 | Cart contains SKU‑A and SKU‑B ($30) | 1. Apply coupon | No discount because second item not SKU‑A; error BOGO_ELIGIBLE_ITEM_MISSING or no change depending on spec |
6.3 Referral and Affiliate Codes
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-056 | Referral code REF123 gives $10 to referrer and $5 to referee on first purchase ≥ $30 | 1. New user signs up via referral link, cart $40 | 1. Apply REF123 → discount $5 for new user, referral log shows $10 credit to referrer |
| CPN-057 | Existing user tries to use own referral code | 1. Apply REF123 | Error SELF_REFERRAL_NOT_ALLOWED |
| CPN-058 | Referee cart $20 (below min) | 1. Apply code | Error MIN_ORDER_NOT_MET (referral still not applied) |
| CPN-059 | Referrer already reached monthly reward cap ($100) | 1. New user completes qualifying purchase | Referrer receives no additional credit; system logs CAP_REACHED |
6.4 Dynamic Pricing and Personalized Offers
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-060 | User receives personalized coupon PERSONAL20 (20% off) based on past purchase category | 1. Log in as targeted user, cart $100 of matching category | 1. Apply coupon → discount $20 |
| CPN-061 | Same user, cart contains items outside targeted category | 1. Apply coupon | Error INELIGIBLE_PRODUCTS |
| CPN-062 | Coupon is single‑use but generated per user; after use, code should be invalid for same user | 1. Apply coupon, complete order 2. Try to apply same code again | Error COUPON_ALREADY_REDEEMED_BY_USER |
| CPN-063 | Coupon is time‑limited to 24h after generation | 1. Generate coupon at 10:00, wait 25h, attempt use | Error 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
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-064 | User 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 order | Order 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-065 | Same as CPN-064 but coupon invalid (expired) | 1. Attempt to apply coupon → error shown 2. Proceed to payment | User cannot proceed to payment until coupon removed or replaced with valid one; order total stays $120 |
| CPN-066 | Cart 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 $70 | Gift card balance reduced by $30, credit card charged $70, shipping $0, order total $100 |
| CPN-067 | Coupon applies to subscription first‑month discount; user selects monthly plan | 1. Apply coupon → first month price $0 (100% off) 2. Complete sign‑up | Subscription created, first invoice $0, subsequent invoices at full price, redemption log linked to subscription ID |
7.2 Payment Gateway Interaction
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-068 | Coupon reduces order to $0.00 (free order) | 1. Apply coupon → total $0 2. Proceed to payment | Payment screen shows “No payment required” or skips payment step entirely; order completes with zero‑dollar transaction |
| CPN-069 | Coupon makes order total negative (due to bug) | 1. Apply coupon → total $-5 2. Attempt payment | System blocks checkout with error INVALID_ORDER_TOTAL (must be ≥ 0) |
| CPN-070 | Coupon 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-071 | Coupon 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
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| CPN-072 | Limited‑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 coupon | System calculates: pay for 4 units, get 2 free → reserves 6 units, inventory decremented by 6 upon order |
| CPN-073 | Same scenario but only 4 units in stock | 1. Add 6 units → stock insufficient error before coupon application | System prevents adding beyond stock; coupon not applied |
| CPN-074 | Coupon provides free item SKU‑Z that is out of stock | 1. Cart meets conditions for free SKU‑Z 2. Apply coupon | Error 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
| Layer | When to Use | Tools (examples) |
|---|---|---|
| Unit | Validate pure calculation functions (discount, eligibility) | JUnit, pytest, Jest |
| API | Test validation endpoint directly, fast and deterministic | Postman/Newman, Rest-Assured, karate |
| UI (Web) | Verify end‑to‑end flow, coupon input UI, error messages | Playwright, Cypress, Selenium |
| UI (Mobile) | Same for native apps, handling soft keyboard, toast | Appium, Espresso, XCUITest |
| Contract | Ensure coupon service API schema stays stable | Pact, Dredd |
| Performance | Check system under load with many coupon validations | k6, 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:
- Generates personas (curious, impatient, novice, etc.) that interact with the coupon field in varied ways (e.g., pasting long strings, using emoji, rapid double‑tap).
- Explores alternative paths: applying a coupon before logging in, after adding a gift card, or mid‑checkout.
- Detects crashes, ANRs, dead buttons, and WCAG issues that might be triggered by
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