How to Write Test Cases for Checkout Process (With Examples)

How to Write Test Cases for Checkout Process (With Examples) is the focus of this article. A checkout flow is the moment where business value is realized, so any defect here directly impacts revenue a

February 16, 2026 · 17 min read · How-To Guides

How to Write Test Cases for Checkout Process (With Examples) is the focus of this article. A checkout flow is the moment where business value is realized, so any defect here directly impacts revenue and brand trust. This guide walks you through a repeatable method for designing test cases that catch the most critical failures, from happy‑path successes to subtle edge conditions that only appear under load or with specific user personas. You will learn how to structure a test case, derive positive and negative scenarios, apply boundary and data‑driven techniques, tie each case to requirements, prioritize effort, and then execute the cases manually or with automated scripts. Finally, we show how an autonomous QA platform can supplement your matrix by exploring the checkout in ways that static scripts often miss.

1. Understanding the Checkout Process and Its Testability

A typical e‑commerce checkout consists of several logical stages: cart review, shipping address entry, shipping method selection, payment information input, order review, and final confirmation. Each stage may involve multiple UI elements, validation rules, asynchronous calls to backend services, and third‑party integrations (payment gateways, tax calculators, inventory systems). Because the flow is stateful and often spans multiple page loads or screen transitions, testability hinges on isolating each stage while preserving the ability to validate end‑to‑end behavior.

1.1. Key Characteristics that Drive Test Design

1.2. Defining Test Scope

Start by enumerating the *testable units* within the checkout:

UnitDescriptionTypical Validation Points
Cart SummaryDisplays items, quantities, subtotal, discountsPrice accuracy, tax application, coupon removal
Shipping AddressForm fields for name, street, city, state, ZIP, countryRequired‑field validation, format checks, address‑verification service
Shipping MethodRadio buttons or dropdown for options (standard, express, same‑day)Correct cost calculation, availability based on address
Payment InformationCard number, expiry, CVV, saved‑payment selection, alternative walletsLuhn check, expiry‑date validation, tokenization call
Order ReviewFinal summary before place orderConsistency with earlier steps, total = subtotal + shipping + tax – discounts
ConfirmationSuccess page, email receipt, order number generationRedirect, HTTP 200, persistence of order in DB, receipt content

By treating each unit as a testable block, you can write focused test cases that later compose into broader scenarios.

2. Anatomy of a High‑Signal Test Case

A test case that delivers maximum signal follows a consistent template. Deviating from this structure leads to ambiguous steps, missing preconditions, or unclear expectations, which in turn cause wasted effort during execution and triage.

2.1. Core Components

  1. Test Case ID – Unique identifier (e.g., CHK‑001) that enables traceability to requirements and test‑management tools.
  2. Title – Concise, imperative phrase describing the scenario (Verify that a valid coupon reduces the order total).
  3. Preconditions – State that must be established before the first step (e.g., “User is logged in, cart contains two items totaling $75”).
  4. Test Data – Specific values used in the steps (card numbers, coupon codes, address fields). Keep data separate from steps whenever possible to aid reuse.
  5. Steps – Numbered actions performed by the tester or automation script. Each step should be atomic, observable, and repeatable.
  6. Expected Result – Observable outcome after the final step (UI message, database entry, API response). Avoid vague statements like “the system works”.
  7. Postconditions (optional) – Any cleanup required (e.g., “Delete the test order from the database”).
  8. Priority / Severity – Indicates execution order and risk impact.
  9. Tags – Keywords for filtering (e.g., @smoke, @payment, @accessibility).

2.2. Writing Effective Steps

2.3. Example Test Case (Template)


ID: CHK-001
Title: Verify that a valid 10% off coupon applies to the subtotal
Preconditions:
  - User is authenticated
  - Cart contains 3 items: Item A ($20), Item B ($15), Item C ($30)
  - No existing coupons applied
Test Data:
  - Coupon code: SAVE10
Steps:
  1. Navigate to Cart page.
  2. Verify subtotal displayed is $65.
  3. Tap the “Apply Coupon” field.
  4. Enter “SAVE10”.
  5. Tap the “Apply” button.
  6. Wait for the coupon confirmation toast.
  7. Verify the discount line shows $6.50.
  8. Verify the new total is $58.50.
Expected Result:
  - Coupon is accepted, discount calculated correctly, order total updated.
Postconditions:
  - Remove the applied coupon to return cart to original state.
Priority: P1 (High)
Tags: @smoke @coupon @positive

3. Positive Test Cases for Checkout

Positive test cases confirm that the system behaves correctly when all inputs are valid and the user follows the intended path. They form the backbone of smoke and regression suites.

3.1. Happy‑Path Scenarios

IDTitlePreconditionsSteps (summarized)Expected Result
CHK-001Complete checkout with a new credit cardGuest user, cart with two items ($45 total)1. Proceed to checkout 2. Fill shipping address 3. Choose shipping method 4. Enter card details 5. Review order 6. Place orderOrder confirmation page shown, order stored, email receipt sent
CHK-002Checkout using a saved payment methodUser logged in, has a saved Visa ending in 4242, cart $1201. Cart → Checkout 2. Shipping address auto‑filled 3. Select saved card 4. Confirm CVV 5. Place orderOrder placed successfully, no re‑entry of card number required
CHK-003Apply a percentage‑off coupon that meets minimum spendUser logged in, cart $80, coupon “SPRING20” (20% off, min $75)1. Cart → Checkout 2. Enter coupon code 3. Apply 4. Review orderDiscount $16 applied, new total $68, coupon accepted
CHK-004Checkout with free shipping promotionUser logged in, cart $100, free‑shipping threshold $75 active1. Proceed to checkout 2. Verify shipping cost $0 3. Complete paymentShipping line shows $0, total reflects only item cost + tax
CHK-005Checkout using an alternative wallet (Apple Pay) on iOSUser on iOS device, Apple Pay configured, cart $601. Tap Apple Pay button 2. Authenticate with Face ID 3. Confirm paymentPayment processed, order confirmation displayed
CHK-006Checkout with multiple quantities of the same SKUGuest user, cart contains 5× Item X ($12 each)1. Review cart (shows $60 subtotal) 2. Continue through steps 3. Place orderOrder line reflects quantity 5, total $60 + tax
CHK-007Checkout with gift‑card partial paymentUser has $25 gift‑card balance, cart $701. Apply gift‑card 2. Pay remaining $45 with card 3. Place orderGift‑card applied, remaining amount charged, order success
CHK-008Checkout with address verification service (AVS) successUser enters a valid US address, AVS enabled1. Fill address 2. System calls AVS 3. ContinueAVS returns match, no error, flow proceeds
CHK-009Checkout with tax calculation for a multi‑state orderUser ships to a state with 8% tax, cart $501. Enter out‑of‑state address 2. Review tax lineTax $4 displayed, total $54
CHK-010Checkout with order review page showing correct totals after editUser modifies quantity in review step1. Go to review 2. Change quantity of Item A from 1 to 3 3. Observe totalsSubtotal, tax, shipping, and grand total update instantly

These ten cases already cover the majority of functional happy paths. Feel free to extend them with additional payment methods (Google Pay, PayPal), different currencies, or loyalty‑point redemption.

3.2. Automation Sketch for Happy Path

Below is a concise Appium (Android) snippet that automates CHK-001. The same logic can be mirrored in Playwright for web.


// Appium Java – Happy‑Path Guest Checkout
@Test
public void testGuestCheckoutWithNewCard() {
    // Precondition: app launched, cart already populated via API
    driver.findElement(By.id("btnProceedToCheckout")).click();

    // Shipping address
    driver.findElement(By.id("etName")).sendKeys("Jane Doe");
    driver.findElement(By.id("etStreet")).sendKeys("123 Main St");
    driver.findElement(By.id("etCity")).sendKeys("Springfield");
    driver.findElement(By.id("etState")).sendKeys("IL");
    driver.findElement(By.id("etZip")).sendKeys("62704");
    driver.findElement(By.id("etCountry")).sendKeys("USA");
    driver.findElement(By.id("btnContinueShipping")).click();

    // Shipping method – select first radio button
    driver.findElement(By.id("rbStandard")).click();
    driver.findElement(By.id("btnContinueMethod")).click();

    // Payment – new card
    driver.findElement(By.id("etCardNumber")).sendKeys("4242424242424242");
    driver.findElement(By.id("etExpDate")).sendKeys("12/25");
    driver.findElement(By.id("etCVV")).sendKeys("123");
    driver.findElement(By.id("btnPlaceOrder")).click();

    // Verify confirmation
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
    wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("tvOrderNumber")));
    Assert.assertTrue(driver.findElement(By.id("tvOrderSuccess")).isDisplayed());
}

A comparable Playwright test for web would look like:


// Playwright – Happy‑Path Guest Checkout (Chromium)
test('guest checkout with new card', async ({ page }) => {
  await page.goto('https://example.shop/cart');
  await page.click('button#proceedToCheckout');

  // Shipping
  await page.fill('input[name="name"]', 'Jane Doe');
  await page.fill('input[name="street"]', '123 Main St');
  await page.fill('input[name="city"]', 'Springfield');
  await page.fill('input[name="state"]', 'IL');
  await page.fill('input[name="zip"]', '62704');
  await page.fill('input[name="country"]', 'USA');
  await page.click('button#continueShipping');

  // Shipping method
  await page.check('input[value="standard"]');
  await page.click('button#continueMethod');

  // Payment
  await page.fill('input[name="cardNumber"]', '4242424242424242');
  await page.fill('input[name="expDate"]', '12/25');
  await page.fill('input[name="cvv"]', '123');
  await page.click('button#placeOrder');

  // Confirmation
  await expect(page.locator('#tvOrderSuccess')).toBeVisible();
  await expect(page.locator('#tvOrderNumber')).toHaveText(/ORD-\d+/);
});

Both snippets illustrate how to translate the test‑case steps into executable code while keeping assertions focused on observable outcomes.

4. Negative and Invalid Input Test Cases

Negative testing proves that the checkout gracefully rejects malformed data, prevents insecure actions, and provides helpful feedback. These cases often uncover defects that slip through positive‑only suites.

4.1. Common Negative Categories

CategoryExample DefectWhy It Matters
Required‑field omissionSubmitting shipping form without ZIPLeads to orders stuck in “address pending” state
Format violationEntering letters in card number fieldMay bypass Luhn check, cause gateway errors
Logical inconsistencySelecting express shipping to a PO‑BoxResults in failed delivery, customer service cost
Business‑rule breachApplying a coupon to a non‑eligible product categoryUndermines promotion integrity, revenue leakage
Security flawSubmitting a card number with spaces that passes frontend but is rejected by gateway, exposing raw number in logsPotential PCI‑DSS violation
Timeout handlingGateway simulation returns 504 after 10 s, UI shows spinner foreverPoor UX, users may abandon cart
Duplicate submissionRapid double‑tap on Place OrderCould create duplicate orders, charge twice

4.2. Negative Test Matrix

IDTitlePreconditionsSteps (key)Expected Result
CHK-N01Submit shipping form with missing ZIP codeGuest user, cart $30, address filled except ZIP1. Fill all other address fields 2. Leave ZIP blank 3. Tap ContinueInline error: “ZIP code is required” (field highlighted)
CHK-N02Enter invalid card number (fails Luhn)User at payment step1. Input “4242 4242 4242 4241” (invalid) 2. Fill expiry/CVV 3. Tap Place OrderError: “Invalid card number” displayed, order not submitted
CHK-N03Use expired cardPayment step1. Input valid number, expiry “01/20” (past), CVV correct 2. Place OrderError: “Card has expired”
CHK-N04Apply coupon that requires minimum spend not metCart $40, coupon “BIGSAVE” (30% off, min $75)1. Enter coupon 2. ApplyMessage: “Coupon requires a minimum purchase of $75”
CHK-N05Select express shipping to a PO‑Box addressAddress line contains “PO Box 123”, shipping method list includes Express1. Choose Express 2. ContinueWarning: “Express shipping not available to PO Box addresses”
CHK-N06Attempt to place order with CVV too short (2 digits)Payment step1. Fill card number/expiry, CVV “12” 2. Place OrderField‑level validation: “CVV must be 3 digits”
CHK-N07Gateway timeout simulation (mock 504)Test environment with gateway mock configured to delay 12 s then return 5041. Proceed to payment 2. Submit card 3. WaitUI shows timeout message, retry button enabled, no order created
CHK-N08Double‑tap Place Order within 200 msPayment step completed1. Rapidly tap Place Order button twiceSystem processes only one request; second tap shows “Order already placed” or is disabled
CHK-N09Enter special characters in name field (e.g., ” 2. ContinueInput sanitized or rejected; no script execution in DOM
CHK-N10Apply coupon after it has reached usage limitCoupon “LOYAL10” limited to 100 uses, already at limit1. Attempt to apply couponMessage: “Coupon code has reached its usage limit”

These negative cases are deliberately orthogonal; each isolates a single validation path so that failures are easy to trace back to a specific rule or service.

4.3. Automation Tips for Negative Cases

When automating negative flows, it is vital to reset the application state after each test to avoid cross‑contamination (e.g., a lingering coupon application affecting the next test). Use API‑based state setup whenever possible:


# Bash helper to reset cart and coupons before each negative test
curl -X POST https://api.example.shop/test/reset \
  -H "Content-Type: application/json" \
  -d '{"clearCart":true,"expireCoupons":false}'

In Appium, you can call this via driver.executeScript("mobile: shell", ...) if the device has access to the backend, or simply hit the REST endpoint from the test runner before each @Test.

For Playwright, leverage test.beforeEach hooks:


test.beforeEach(async ({ request }) => {
  await request.post('https://api.example.shop/test/reset', {
    data: JSON.stringify({ clearCart: true, expireCoupons: false })
  });
});

5. Boundary, Edge, and Data‑Driven Cases

Boundary testing targets the limits of input fields, while edge cases combine multiple constraints or rare user behaviors. Data‑driven testing lets you execute the same logical steps with many value sets efficiently.

5.1. Field‑Level Boundary Values

FieldMinimumMaximumTypical ValidInvalid BelowInvalid Above
Quantity per SKU19950100
ZIP (US)5 digits5 digits902101234123456
Card number length1319161220
Expiry month0112060013
Expiry yearcurrentcurrent+102025lastYear-1current+11
CVV34123121234
Coupon code length312SUMMER20ABTHISISLONGCODE

Create test cases that hit each boundary (min, min‑1, max, max+1) for at least the most risk‑prone fields: quantity, ZIP, card number length, and expiry.

5.2. Example Boundary Test Cases

IDTitlePreconditionsSteps (summary)Expected Result
CHK-B01Quantity = 0 triggers validation errorCart with one item, quantity selector visible1. Decrement quantity to 0 2. Attempt to proceed to checkoutInline error: “Quantity must be at least 1”
CHK-B02Quantity = 100 (above max) rejectedSame as above1. Increment quantity to 100 2. Try to checkoutError: “Maximum quantity per item is 99”
CHK-B03ZIP code with 4 digits rejectedShipping address form1. Enter ZIP “1234” 2. ContinueField error: “ZIP must be 5 digits”
CHK-B04ZIP code with 6 digits rejectedSame1. Enter ZIP “123456” 2. ContinueSame error as above
CHK-B05Card number length 13 (minimum Amex) acceptedPayment step, using a valid 13‑digit test number1. Enter 13‑digit number, correct expiry/CVV 2. Place OrderOrder succeeds (if gateway accepts)
CHK-B06Card number length 20 (above max) rejectedPayment step1. Enter 20‑digit number 2. Place OrderError: “Invalid card number”
CHK-B07Expiry month 00 rejectedPayment step1. Set month to “00” 2. Fill other fields 3. Place OrderError: “Invalid expiry month”
CHK-B08Expiry year set to last year (expired) rejectedPayment step1. Set year to previous year 2. Place OrderError: “Card has expired”
CHK-B09Coupon code length 2 rejectedCart meets coupon min spend1. Enter coupon “AB” 2. ApplyError: “Invalid coupon code”
CHK-B10Coupon code length 13 rejectedSame1. Enter coupon “THISISLONGCODE” 2. ApplySame error

5.3. Edge‑Case Scenarios

Edge cases often involve combinations of boundary values, state transitions, or rare user behaviors such as network interruptions mid‑flow, switching between guest and logged‑in states, or using assistive technology.

IDTitlePreconditionsSteps (summary)Expected Result
CHK-E01User logs in after entering shipping address, address persistsGuest user, shipping address partially filled1. Fill street, city, state, ZIP 2. Tap “Sign In” 3. Log in with existing account 4. Continue to paymentShipping fields remain filled with the previously entered data
CHK-E02Network loss after submitting payment, retry succeedsMock gateway configured to return 500 on first call, 200 on retry1. Submit payment 2. Observe error toast 3. Tap “Retry” 4. Wait for successOrder placed successfully after retry, no duplicate charge
CHK-E03User switches from guest to logged‑in mid‑checkout, cart mergesGuest cart $30, logged‑in user has wishlist item $201. Guest checkout started 2. Tap “Sign In” 3. Log in 4. Review cartCart shows both guest‑added items and wishlist item, total $50
CHK-E04Screen reader announces all required fields and errorsTalkBack enabled on Android, VoiceOver on iOS1. Navigate to checkout 2. Focus each field 3. Listen for announcementsEach field announces its label, state, and any error message
CHK-E05User attempts to apply coupon while keyboard is open, layout does not shiftMobile device, soft keyboard visible1. Tap coupon field 2. Keyboard appears 3. Attempt to tap Apply buttonApply button remains visible and tappable; no UI clipping
CHK-E06Order placed with exactly the minimum order value for free shippingCart $74.99, free‑shipping threshold $75, coupon “FIVESAVE” ($5 off)1. Apply coupon (total $69.99) 2. Add low‑cost item $5.01 to reach $75 3. Choose free shipping 4. Place orderShipping cost $0, total reflects items + tax, free shipping applied
CHK-E07Gift‑card balance exceeds order total, remainder stays on cardGift‑card balance $100, order $651. Apply gift‑card 2. Attempt to pay remaining with card (should be skipped) 3. Place orderNo payment gateway call for card, order success, gift‑card balance $35 remaining
CHK-E08User changes shipping method after address validation, tax updatesShipping to a state with tax, address validated1. Choose standard shipping (tax $4) 2. Change to express (tax $6) 3. Review orderTax line updates accordingly, total reflects new shipping cost + tax
CHK-E09Applying a coupon that is product‑specific to an ineligible SKUCoupon “BOOK10” valid only on books, cart contains a t‑shirt1. Enter coupon 2. ApplyMessage: “Coupon not valid for items in your cart”
CHK-E10Order confirmation page shows correct order number formatAny successful order1. Complete checkout 2. On confirmation page inspect order numberOrder number matches regex ORD-[A-Z0-9]{8} (or your spec)

5.4. Data‑Driven Testing Approach

Instead of writing a separate test for each boundary value, feed a CSV or JSON file into your test framework. Below is a Playwright example that iterates over quantity boundaries.


// test-data/quantity-boundaries.csv
// quantity,expectedResult
// 0,error
// 1,success
// 99,success
// 100,error

import { test, expect } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';

const data = fs.readFileSync(path.resolve(__dirname, 'test-data/quantity-boundaries.csv'), 'utf8')
  .trim()
  .split('\n')
  .map(line => line.split(',').map(cell => cell.trim()));

test.describe('Quantity boundary validation', () => {
  for (const [qty, expected] of data.slice(1)) { // skip header
    test(`Quantity ${qty} should ${expected === 'success' ? 'succeed' : 'fail'}`, async ({ page }) => {
      await page.goto('https://example.shop/product/123');
      await page.fill('input[name="quantity"]', qty);
      await page.click('button#addToCart');
      await page.click('button#proceedToCheckout');

      if (expected === 'success') {
        await expect(page.locator('#tvOrderSuccess')).toBeVisible();
      } else {
        const error = page.locator('.field-error');
        await expect(error).toBeVisible();
        await expect(error).toHaveText(/Quantity/);
      }
    });
  }
});

This pattern scales to dozens of fields with minimal duplication. Keep the data files version‑controlled alongside your test code so that reviewers can see exactly which values are being exercised.

6. Prioritization, Traceability, and Test‑Case Management

A large test matrix can become unwieldy if not organized. Prioritization ensures that the most critical paths receive frequent execution, while traceability links each test to a requirement, user story, or risk item.

6.1. Prioritization Framework

Use a simple Risk × Impact matrix:

PriorityDefinitionTypical Execution Frequency
P1 (Critical)Failure blocks revenue, causes financial loss, or violates compliance (e.g., payment processing, order creation)Run on every build, part of smoke suite
P2 (High)Causes significant user frustration or operational overhead (e.g., shipping‑method miscalculation, coupon misapplication)Run nightly or on every release candidate
P3 (Medium)Affects edge‑case usability or cosmetic issues (e.g., placeholder text, minor layout shift)Run weekly or before major releases
P4 (Low)Nice‑to‑have, low risk (e.g., optional analytics event)Run on demand or in periodic regression

Assign each test case a priority based on the failure mode it validates. For example, CHK-001 (happy‑path order placement) is P1, whereas CHK-E04 (screen‑reader announcement) might be P3 unless accessibility is a legal requirement for your market.

6.2. Traceability Matrix

Create a lightweight spreadsheet or use your test‑management tool’s linking feature. Columns: Test ID, Requirement ID, User Story, Feature Area, Priority, Last Executed, Result.

Test IDRequirement IDUser StoryFeature AreaPriority
CHK-001REQ‑ORD‑01AS‑001: Guest checkoutOrder placementP1
CHK-N02REQ‑PAY‑03AS‑007: Card validationPayment inputP1
CHK-B03REQ‑SHIP‑02AS‑012: ZIP validationShipping addressP2
CHK-E04REQ‑ACC‑05AS‑020: Screen‑reader supportAccessibilityP3
CHK-E06REQ‑PROM‑04AS‑015: Free‑shipping thresholdPromotionsP2

When a requirement changes, you can quickly identify which tests need review or retirement. This also satisfies audit needs for regulated industries (e.g., PCI‑DSS, GDPR).

6.3. Test‑Case Maintenance Tips

7. Manual Execution vs. Automated Scripts (Appium + Playwright)

Both manual and automated testing have roles in a robust checkout validation strategy. Manual testing excels at exploratory, usability, and ad‑hoc scenarios; automation shines for repeatable regression, performance, and data‑driven suites.

7.1. When to Test Manually

ScenarioReason for Manual
First‑time feature explorationHuman intuition spots unexpected flows (e.g., trying to use a gift‑card after a promo code).

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