🔥 0
0
Lesson 7 of 10 30 min +250 XP

E-Commerce Load Scenarios

You've learned the individual JMeter components. Now let's combine them into realistic e-commerce test scenarios that reveal how your system behaves under real-world conditions.

Understanding E-commerce Traffic Patterns

Real e-commerce traffic isn't uniform. Different users do different things:

70%
Browse only
20%
Add to cart, abandon
10%
Complete purchase

Your test should reflect this reality.

Scenario 1: Complete User Journey

The most important test. Simulates a user from landing to order confirmation.

User Flow

1. Visit Homepage
2. Browse Category (Electronics)
3. View Product List
4. View Product Details (2-3 products)
5. Add Product to Cart
6. View Cart
7. Update Quantity
8. Proceed to Checkout
9. Enter Shipping Info
10. Enter Payment
11. Confirm Order
12. View Order Confirmation

Test Plan Structure

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2">
  <hashTree>
    <TestPlan testname="E-Commerce Complete Journey">
      <boolProp name="TestPlan.functional_mode">false</boolProp>
    </TestPlan>
    <hashTree>
      <!-- Configuration Elements -->
      <ConfigTestElement testname="HTTP Request Defaults">
        <stringProp name="HTTPSampler.domain">api.mystore.com</stringProp>
        <stringProp name="HTTPSampler.protocol">https</stringProp>
        <stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
      </ConfigTestElement>

      <CSVDataSet testname="User Credentials">
        <stringProp name="filename">test-data/users.csv</stringProp>
        <stringProp name="variableNames">email,password,userId</stringProp>
        <boolProp name="recycle">false</boolProp>
        <stringProp name="shareMode">shareMode.all</stringProp>
      </CSVDataSet>

      <CSVDataSet testname="Product Data">
        <stringProp name="filename">test-data/products.csv</stringProp>
        <stringProp name="variableNames">productId,productName,price</stringProp>
        <boolProp name="recycle">true</boolProp>
      </CSVDataSet>

      <!-- Main Thread Group -->
      <ThreadGroup testname="Shoppers">
        <stringProp name="ThreadGroup.num_threads">100</stringProp>
        <stringProp name="ThreadGroup.ramp_time">60</stringProp>
        <boolProp name="ThreadGroup.scheduler">true</boolProp>
        <stringProp name="ThreadGroup.duration">600</stringProp>
      </ThreadGroup>
      <hashTree>
        <!-- Transaction Controller groups requests into logical steps -->
        <TransactionController testname="TC_01_Browse_Products">
          <boolProp name="TransactionController.includeTimers">false</boolProp>
        </TransactionController>
        <hashTree>
          <HTTPSamplerProxy testname="Get Categories">
            <stringProp name="HTTPSampler.path">/api/categories</stringProp>
            <stringProp name="HTTPSampler.method">GET</stringProp>
          </HTTPSamplerProxy>

          <GaussianRandomTimer testname="Browse Think Time">
            <stringProp name="ConstantTimer.delay">3000</stringProp>
            <stringProp name="RandomTimer.range">2000</stringProp>
          </GaussianRandomTimer>

          <HTTPSamplerProxy testname="Get Products">
            <stringProp name="HTTPSampler.path">/api/products?category=electronics</stringProp>
            <stringProp name="HTTPSampler.method">GET</stringProp>
          </HTTPSamplerProxy>
          <hashTree>
            <JSONPostProcessor testname="Extract First Product">
              <stringProp name="JSONPostProcessor.referenceNames">selectedProductId</stringProp>
              <stringProp name="JSONPostProcessor.jsonPathExprs">$.products[0].id</stringProp>
            </JSONPostProcessor>
          </hashTree>
        </hashTree>

        <TransactionController testname="TC_02_View_Product">
          <boolProp name="TransactionController.includeTimers">false</boolProp>
        </TransactionController>
        <hashTree>
          <GaussianRandomTimer testname="Reading Product List">
            <stringProp name="ConstantTimer.delay">5000</stringProp>
            <stringProp name="RandomTimer.range">3000</stringProp>
          </GaussianRandomTimer>

          <HTTPSamplerProxy testname="Get Product Details">
            <stringProp name="HTTPSampler.path">/api/products/${selectedProductId}</stringProp>
            <stringProp name="HTTPSampler.method">GET</stringProp>
          </HTTPSamplerProxy>
          <hashTree>
            <JSONPostProcessor testname="Extract Product Info">
              <stringProp name="JSONPostProcessor.referenceNames">productPrice;stockLevel</stringProp>
              <stringProp name="JSONPostProcessor.jsonPathExprs">$.price;$.stockLevel</stringProp>
            </JSONPostProcessor>
          </hashTree>
        </hashTree>

        <TransactionController testname="TC_03_Add_To_Cart">
          <boolProp name="TransactionController.includeTimers">false</boolProp>
        </TransactionController>
        <hashTree>
          <GaussianRandomTimer testname="Decision Time">
            <stringProp name="ConstantTimer.delay">8000</stringProp>
            <stringProp name="RandomTimer.range">5000</stringProp>
          </GaussianRandomTimer>

          <HTTPSamplerProxy testname="Add To Cart">
            <stringProp name="HTTPSampler.path">/api/cart/items</stringProp>
            <stringProp name="HTTPSampler.method">POST</stringProp>
            <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
            <stringProp name="Argument.value">{
              "productId": "${selectedProductId}",
              "quantity": ${__Random(1,3)}
            }</stringProp>
          </HTTPSamplerProxy>
          <hashTree>
            <JSONPostProcessor testname="Extract Cart ID">
              <stringProp name="JSONPostProcessor.referenceNames">cartId</stringProp>
              <stringProp name="JSONPostProcessor.jsonPathExprs">$.cartId</stringProp>
            </JSONPostProcessor>
          </hashTree>
        </hashTree>

        <TransactionController testname="TC_04_Checkout">
          <boolProp name="TransactionController.includeTimers">false</boolProp>
        </TransactionController>
        <hashTree>
          <HTTPSamplerProxy testname="Get Cart">
            <stringProp name="HTTPSampler.path">/api/cart/${cartId}</stringProp>
            <stringProp name="HTTPSampler.method">GET</stringProp>
          </HTTPSamplerProxy>

          <GaussianRandomTimer testname="Review Cart">
            <stringProp name="ConstantTimer.delay">5000</stringProp>
            <stringProp name="RandomTimer.range">3000</stringProp>
          </GaussianRandomTimer>

          <HTTPSamplerProxy testname="Start Checkout">
            <stringProp name="HTTPSampler.path">/api/checkout/start</stringProp>
            <stringProp name="HTTPSampler.method">POST</stringProp>
            <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
            <stringProp name="Argument.value">{"cartId": "${cartId}"}</stringProp>
          </HTTPSamplerProxy>
          <hashTree>
            <JSONPostProcessor testname="Extract Checkout Session">
              <stringProp name="JSONPostProcessor.referenceNames">checkoutSessionId</stringProp>
              <stringProp name="JSONPostProcessor.jsonPathExprs">$.sessionId</stringProp>
            </JSONPostProcessor>
          </hashTree>

          <GaussianRandomTimer testname="Fill Shipping Form">
            <stringProp name="ConstantTimer.delay">30000</stringProp>
            <stringProp name="RandomTimer.range">15000</stringProp>
          </GaussianRandomTimer>

          <HTTPSamplerProxy testname="Submit Shipping">
            <stringProp name="HTTPSampler.path">/api/checkout/${checkoutSessionId}/shipping</stringProp>
            <stringProp name="HTTPSampler.method">POST</stringProp>
            <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
            <stringProp name="Argument.value">{
              "address": "${shippingAddress}",
              "city": "${city}",
              "zipCode": "${zipCode}"
            }</stringProp>
          </HTTPSamplerProxy>

          <GaussianRandomTimer testname="Enter Payment">
            <stringProp name="ConstantTimer.delay">20000</stringProp>
            <stringProp name="RandomTimer.range">10000</stringProp>
          </GaussianRandomTimer>

          <HTTPSamplerProxy testname="Submit Payment">
            <stringProp name="HTTPSampler.path">/api/checkout/${checkoutSessionId}/payment</stringProp>
            <stringProp name="HTTPSampler.method">POST</stringProp>
            <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
            <stringProp name="Argument.value">{
              "paymentMethodId": "${paymentMethodId}",
              "saveCard": false
            }</stringProp>
          </HTTPSamplerProxy>
          <hashTree>
            <JSONPostProcessor testname="Extract Order ID">
              <stringProp name="JSONPostProcessor.referenceNames">orderId</stringProp>
              <stringProp name="JSONPostProcessor.jsonPathExprs">$.orderId</stringProp>
            </JSONPostProcessor>

            <JSONPathAssertion testname="Verify Order Created">
              <stringProp name="JSON_PATH">$.status</stringProp>
              <stringProp name="EXPECTED_VALUE">confirmed</stringProp>
              <boolProp name="JSONVALIDATION">true</boolProp>
            </JSONPathAssertion>
          </hashTree>
        </hashTree>

        <TransactionController testname="TC_05_Order_Confirmation">
          <boolProp name="TransactionController.includeTimers">false</boolProp>
        </TransactionController>
        <hashTree>
          <HTTPSamplerProxy testname="Get Order Details">
            <stringProp name="HTTPSampler.path">/api/orders/${orderId}</stringProp>
            <stringProp name="HTTPSampler.method">GET</stringProp>
          </HTTPSamplerProxy>
          <hashTree>
            <JSONPathAssertion testname="Verify Order ID Matches">
              <stringProp name="JSON_PATH">$.id</stringProp>
              <stringProp name="EXPECTED_VALUE">${orderId}</stringProp>
              <boolProp name="JSONVALIDATION">true</boolProp>
            </JSONPathAssertion>
          </hashTree>
        </hashTree>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

Scenario 2: Mixed User Types

More realistic test with different user behaviors.

Thread Group Configuration

<!-- Browsers: 70% of traffic - just look, don't buy -->
<ThreadGroup testname="Browsers">
  <stringProp name="ThreadGroup.num_threads">700</stringProp>
  <stringProp name="ThreadGroup.ramp_time">120</stringProp>
  <stringProp name="ThreadGroup.duration">1800</stringProp>
</ThreadGroup>
<hashTree>
  <!-- Only browse products, no cart operations -->
  <HTTPSamplerProxy testname="Browse Homepage"/>
  <HTTPSamplerProxy testname="Browse Category"/>
  <HTTPSamplerProxy testname="View Product 1"/>
  <HTTPSamplerProxy testname="View Product 2"/>
  <HTTPSamplerProxy testname="View Product 3"/>
  <!-- Exit without purchase -->
</hashTree>

<!-- Cart Abandoners: 20% - add to cart but don't complete -->
<ThreadGroup testname="Cart Abandoners">
  <stringProp name="ThreadGroup.num_threads">200</stringProp>
  <stringProp name="ThreadGroup.ramp_time">120</stringProp>
  <stringProp name="ThreadGroup.duration">1800</stringProp>
</ThreadGroup>
<hashTree>
  <HTTPSamplerProxy testname="Browse Products"/>
  <HTTPSamplerProxy testname="Add to Cart"/>
  <HTTPSamplerProxy testname="View Cart"/>
  <!-- Start checkout but abandon -->
  <HTTPSamplerProxy testname="Start Checkout"/>
  <!-- Exit without completing -->
</hashTree>

<!-- Buyers: 10% - complete full purchase -->
<ThreadGroup testname="Buyers">
  <stringProp name="ThreadGroup.num_threads">100</stringProp>
  <stringProp name="ThreadGroup.ramp_time">120</stringProp>
  <stringProp name="ThreadGroup.duration">1800</stringProp>
</ThreadGroup>
<hashTree>
  <!-- Complete journey from browse to purchase -->
  <HTTPSamplerProxy testname="Browse"/>
  <HTTPSamplerProxy testname="Add to Cart"/>
  <HTTPSamplerProxy testname="Checkout"/>
  <HTTPSamplerProxy testname="Payment"/>
  <HTTPSamplerProxy testname="Confirmation"/>
</hashTree>

Scenario 3: Flash Sale Spike Test

The critical test for marketing events.

Flash Sale Traffic Pattern

Time      | Users  | Event
----------|--------|----------------------------------
0:00      | 500    | Normal traffic baseline
5:00      | 500    | Still normal
5:30      | 8000   | Sale announced, rush begins
5:35      | 10000  | Peak rush
6:00      | 8000   | Sustained high load
15:00     | 6000   | Gradual decline as stock depletes
30:00     | 1000   | Sale ends, return to normal

Flash Sale Test Plan

<!-- Baseline Traffic - Always running -->
<ThreadGroup testname="Normal Shoppers">
  <stringProp name="ThreadGroup.num_threads">500</stringProp>
  <stringProp name="ThreadGroup.ramp_time">60</stringProp>
  <boolProp name="ThreadGroup.scheduler">true</boolProp>
  <stringProp name="ThreadGroup.duration">1800</stringProp>
  <stringProp name="ThreadGroup.delay">0</stringProp>
</ThreadGroup>

<!-- Flash Sale Rush Wave 1 - Starts at 5 minutes -->
<ThreadGroup testname="Flash Sale Wave 1">
  <stringProp name="ThreadGroup.num_threads">3000</stringProp>
  <stringProp name="ThreadGroup.ramp_time">30</stringProp>
  <boolProp name="ThreadGroup.scheduler">true</boolProp>
  <stringProp name="ThreadGroup.duration">600</stringProp>
  <stringProp name="ThreadGroup.delay">300</stringProp>
</ThreadGroup>

<!-- Flash Sale Rush Wave 2 - Starts at 5.5 minutes -->
<ThreadGroup testname="Flash Sale Wave 2">
  <stringProp name="ThreadGroup.num_threads">4500</stringProp>
  <stringProp name="ThreadGroup.ramp_time">30</stringProp>
  <boolProp name="ThreadGroup.scheduler">true</boolProp>
  <stringProp name="ThreadGroup.duration">570</stringProp>
  <stringProp name="ThreadGroup.delay">330</stringProp>
</ThreadGroup>

<!-- Sustained Load - After initial spike -->
<ThreadGroup testname="Sustained Sale Traffic">
  <stringProp name="ThreadGroup.num_threads">2000</stringProp>
  <stringProp name="ThreadGroup.ramp_time">60</stringProp>
  <boolProp name="ThreadGroup.scheduler">true</boolProp>
  <stringProp name="ThreadGroup.duration">900</stringProp>
  <stringProp name="ThreadGroup.delay">360</stringProp>
</ThreadGroup>

Flash Sale Specific Tests

<hashTree>
  <!-- Flash sale users go straight to deal products -->
  <HTTPSamplerProxy testname="Get Flash Sale Products">
    <stringProp name="HTTPSampler.path">/api/flash-sale/products</stringProp>
    <stringProp name="HTTPSampler.method">GET</stringProp>
  </HTTPSamplerProxy>

  <!-- Minimal think time - users are racing -->
  <ConstantTimer testname="Quick Decision">
    <stringProp name="ConstantTimer.delay">500</stringProp>
  </ConstantTimer>

  <HTTPSamplerProxy testname="Add Sale Item to Cart">
    <stringProp name="HTTPSampler.path">/api/cart/items</stringProp>
    <stringProp name="HTTPSampler.method">POST</stringProp>
    <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
    <stringProp name="Argument.value">{
      "productId": "${saleProductId}",
      "quantity": 1,
      "flashSaleId": "${flashSaleId}"
    }</stringProp>
  </HTTPSamplerProxy>
  <hashTree>
    <!-- Flash sales often have inventory race conditions -->
    <JSONPathAssertion testname="Check Stock Available">
      <stringProp name="JSON_PATH">$.success</stringProp>
      <stringProp name="EXPECTED_VALUE">true</stringProp>
      <boolProp name="JSONVALIDATION">true</boolProp>
    </JSONPathAssertion>
  </hashTree>

  <!-- Immediate checkout - no browsing -->
  <HTTPSamplerProxy testname="Express Checkout">
    <stringProp name="HTTPSampler.path">/api/checkout/express</stringProp>
    <stringProp name="HTTPSampler.method">POST</stringProp>
  </HTTPSamplerProxy>
</hashTree>

Scenario 4: Cart Stress Test

Test cart operations under heavy concurrent access.

<ThreadGroup testname="Cart Stress">
  <stringProp name="ThreadGroup.num_threads">500</stringProp>
  <stringProp name="ThreadGroup.ramp_time">30</stringProp>
  <elementProp name="ThreadGroup.main_controller" elementType="LoopController">
    <stringProp name="LoopController.loops">20</stringProp>
  </elementProp>
</ThreadGroup>
<hashTree>
  <!-- Create cart -->
  <HTTPSamplerProxy testname="Create Cart">
    <stringProp name="HTTPSampler.path">/api/cart</stringProp>
    <stringProp name="HTTPSampler.method">POST</stringProp>
  </HTTPSamplerProxy>

  <!-- Add multiple items rapidly -->
  <LoopController testname="Add Items Loop">
    <stringProp name="LoopController.loops">5</stringProp>
  </LoopController>
  <hashTree>
    <HTTPSamplerProxy testname="Add Item">
      <stringProp name="HTTPSampler.path">/api/cart/${cartId}/items</stringProp>
      <stringProp name="HTTPSampler.method">POST</stringProp>
      <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
      <stringProp name="Argument.value">{
        "productId": "${__CSVRead(products.csv,0)}",
        "quantity": ${__Random(1,3)}
      }</stringProp>
    </HTTPSamplerProxy>

    <ConstantTimer testname="Quick Add">
      <stringProp name="ConstantTimer.delay">200</stringProp>
    </ConstantTimer>
  </hashTree>

  <!-- Update quantities -->
  <HTTPSamplerProxy testname="Update Quantities">
    <stringProp name="HTTPSampler.path">/api/cart/${cartId}/items/${itemId}</stringProp>
    <stringProp name="HTTPSampler.method">PATCH</stringProp>
    <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
    <stringProp name="Argument.value">{"quantity": ${__Random(1,10)}}</stringProp>
  </HTTPSamplerProxy>

  <!-- Remove item -->
  <HTTPSamplerProxy testname="Remove Item">
    <stringProp name="HTTPSampler.path">/api/cart/${cartId}/items/${itemId}</stringProp>
    <stringProp name="HTTPSampler.method">DELETE</stringProp>
  </HTTPSamplerProxy>

  <!-- Get cart total (tests calculation under load) -->
  <HTTPSamplerProxy testname="Get Cart Total">
    <stringProp name="HTTPSampler.path">/api/cart/${cartId}/total</stringProp>
    <stringProp name="HTTPSampler.method">GET</stringProp>
  </HTTPSamplerProxy>
  <hashTree>
    <JSONPathAssertion testname="Total Is Positive">
      <stringProp name="JSON_PATH">$.total</stringProp>
      <stringProp name="EXPECTED_VALUE">0</stringProp>
      <boolProp name="JSONVALIDATION">true</boolProp>
    </JSONPathAssertion>
  </hashTree>
</hashTree>

Using Transaction Controllers

Group related requests to measure end-to-end transaction time.

<TransactionController testname="Complete Purchase Flow">
  <boolProp name="TransactionController.includeTimers">false</boolProp>
  <boolProp name="TransactionController.parent">true</boolProp>
</TransactionController>
<hashTree>
  <!-- All checkout steps -->
  <HTTPSamplerProxy testname="Start Checkout"/>
  <HTTPSamplerProxy testname="Add Shipping"/>
  <HTTPSamplerProxy testname="Add Payment"/>
  <HTTPSamplerProxy testname="Confirm Order"/>
</hashTree>

Results show both individual request times AND the total transaction time.

Key Takeaways

  • Mirror reality: Use multiple thread groups to simulate browsers (70%), abandoners (20%), and buyers (10%).
  • Test complete journeys: Individual API tests miss integration issues that emerge in realistic flows.
  • Flash sales need spike tests: Simulate sudden 10-20x traffic increases with minimal think time.
  • Use Transaction Controllers: Measure end-to-end user experience, not just individual requests.

Next Lesson

These scenarios test your web application. But modern e-commerce often relies on APIs, microservices, and third-party integrations. Next: API Load Testing - testing REST APIs, handling JWT authentication, and validating microservices under load.

🧠 Quick Quiz

Test your understanding of this lesson.

1

Why should you test the complete user journey, not just individual APIs?

2

What's the typical ratio of browsers to buyers in e-commerce?

3

What is the key characteristic of a flash sale test?