Testing the Shopping Cart
Why Cart Testing Matters
The shopping cart is revenue-critical. Cart bugs directly impact:
- Conversion rates
- Customer trust
- Revenue accuracy
- Inventory management
> "A bug in the checkout flow costs you money. A bug in the About page costs you almost nothing."
Cart Test Categories
Prioritize your cart testing efforts based on risk and impact:
CRITICAL (Must automate): Add to cart, remove, update quantity, total calculation, proceed to checkout IMPORTANT (Should automate): Cart persistence, out of stock handling, max quantity limits, guest vs logged-in, promo codes NICE TO HAVE (Manual or selective): Cart animations, cross-sell suggestions, save for laterTest Cases: Add to Cart
# tests/test_add_to_cart.py
import pytest
from pages.products_page import ProductsPage
from pages.components.header import Header
class TestAddToCart:
def test_add_single_product(self, driver):
"""
Given: User is on products page
When: User clicks Add to Cart on a product
Then: Cart count increases by 1
"""
products = ProductsPage(driver).open()
header = Header(driver)
initial_count = header.get_cart_count()
products.add_product_to_cart(0)
assert header.get_cart_count() == initial_count + 1
def test_add_multiple_different_products(self, driver):
"""
Given: User adds Product A to cart
When: User adds Product B to cart
Then: Cart shows 2 items
"""
products = ProductsPage(driver).open()
header = Header(driver)
products.add_product_to_cart(0)
products.add_product_to_cart(1)
assert header.get_cart_count() == 2
def test_add_same_product_twice(self, driver):
"""
Given: User adds Product A to cart
When: User adds Product A again
Then: Cart shows 1 item with quantity 2
"""
products = ProductsPage(driver).open()
products.add_product_to_cart(0)
products.add_product_to_cart(0) # Same product
cart = Header(driver).go_to_cart()
assert cart.get_item_count() == 1
assert cart.get_item_by_index(0).quantity == 2
def test_add_to_cart_shows_confirmation(self, driver):
"""
Given: User clicks Add to Cart
Then: Confirmation message appears
And: Confirmation disappears after a few seconds
"""
products = ProductsPage(driver).open()
products.add_product_to_cart(0)
# Check confirmation appears
assert products.is_visible(products.ADD_CONFIRMATION)
# Check it disappears (wait up to 5 seconds)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait(driver, 5).until(
EC.invisibility_of_element_located(products.ADD_CONFIRMATION)
)
def test_add_to_cart_button_state_change(self, driver):
"""
Given: User adds product to cart
Then: Button changes to "Added" or shows checkmark
"""
products = ProductsPage(driver).open()
product = products.get_product_by_index(0)
# Get button before
add_btn = product.find_element(*products.ADD_TO_CART_BTN)
original_text = add_btn.text
products.add_product_to_cart(0)
# Button should change
new_text = add_btn.text
assert new_text != original_text or "Added" in new_text
Test Cases: Remove from Cart
# tests/test_remove_from_cart.py
class TestRemoveFromCart:
def test_remove_single_item(self, driver, cart_with_one_item):
"""
Given: Cart has 1 item
When: User clicks Remove
Then: Cart is empty
"""
cart = cart_with_one_item
cart.remove_item(0)
assert cart.is_empty()
assert cart.get_item_count() == 0
def test_remove_one_of_multiple_items(self, driver, cart_with_three_items):
"""
Given: Cart has 3 items
When: User removes item at index 1
Then: Cart has 2 items
And: Removed item is no longer present
"""
cart = cart_with_three_items
item_to_remove = cart.get_item_by_index(1).title
cart.remove_item(1)
assert cart.get_item_count() == 2
remaining_titles = [cart.get_item_by_index(i).title
for i in range(cart.get_item_count())]
assert item_to_remove not in remaining_titles
def test_remove_updates_total(self, driver, cart_with_items):
"""
Given: Cart has items with total $X
When: User removes an item worth $Y
Then: New total is $X - $Y
"""
cart = cart_with_items
original_total = cart.get_total()
item_price = cart.get_item_by_index(0).price
cart.remove_item(0)
new_total = cart.get_total()
assert abs(new_total - (original_total - item_price)) < 0.01
def test_clear_cart(self, driver, cart_with_three_items):
"""
Given: Cart has multiple items
When: User removes all items
Then: Empty cart message is shown
"""
cart = cart_with_three_items
while cart.get_item_count() > 0:
cart.remove_item(0)
assert cart.is_empty()
assert "empty" in cart.get_text(cart.EMPTY_CART_MSG).lower()
Test Cases: Update Quantity
# tests/test_update_quantity.py
class TestUpdateQuantity:
def test_increase_quantity(self, driver, cart_with_one_item):
"""
Given: Cart has 1 item with quantity 1
When: User changes quantity to 3
Then: Quantity is updated to 3
"""
cart = cart_with_one_item
cart.update_quantity(0, 3)
assert cart.get_item_by_index(0).quantity == 3
def test_decrease_quantity(self, driver, cart_with_one_item):
"""
Given: Cart has 1 item with quantity 3
When: User changes quantity to 1
Then: Quantity is updated to 1
"""
cart = cart_with_one_item
cart.update_quantity(0, 3) # Setup: set to 3
cart.update_quantity(0, 1) # Test: decrease to 1
assert cart.get_item_by_index(0).quantity == 1
def test_quantity_updates_subtotal(self, driver, cart_with_one_item):
"""
Given: Item costs $10
When: User changes quantity to 3
Then: Item subtotal shows $30
"""
cart = cart_with_one_item
item = cart.get_item_by_index(0)
unit_price = item.price
cart.update_quantity(0, 3)
# Verify subtotal (item price * quantity)
expected_subtotal = unit_price * 3
actual_subtotal = item.subtotal # Need to add this property
assert abs(actual_subtotal - expected_subtotal) < 0.01
def test_quantity_zero_removes_item(self, driver, cart_with_one_item):
"""
Given: Cart has 1 item
When: User changes quantity to 0
Then: Item is removed from cart
"""
cart = cart_with_one_item
cart.update_quantity(0, 0)
assert cart.is_empty() or cart.get_item_count() == 0
def test_quantity_exceeds_stock(self, driver, cart_with_one_item):
"""
Given: Item has 5 in stock
When: User tries to set quantity to 10
Then: Error message is shown
And: Quantity is capped at 5
"""
cart = cart_with_one_item
cart.update_quantity(0, 10) # Exceeds stock
# Check for error or warning
assert cart.is_visible(cart.QUANTITY_ERROR) or \
cart.get_item_by_index(0).quantity <= 5
Test Cases: Cart Persistence
# tests/test_cart_persistence.py
class TestCartPersistence:
def test_cart_persists_on_page_refresh(self, driver, cart_with_one_item):
"""
Given: Cart has 1 item
When: User refreshes the page
Then: Cart still has 1 item
"""
cart = cart_with_one_item
item_title = cart.get_item_by_index(0).title
driver.refresh()
# Re-instantiate page object after refresh
from pages.cart_page import CartPage
cart = CartPage(driver)
assert cart.get_item_count() == 1
assert cart.get_item_by_index(0).title == item_title
def test_cart_persists_in_new_tab(self, driver, cart_with_one_item):
"""
Given: Cart has items in Tab 1
When: User opens cart in new tab
Then: New tab shows same cart contents
"""
cart = cart_with_one_item
original_count = cart.get_item_count()
# Open new tab
driver.execute_script("window.open('');")
driver.switch_to.window(driver.window_handles[1])
# Navigate to cart in new tab
from pages.cart_page import CartPage
cart_new_tab = CartPage(driver).open()
assert cart_new_tab.get_item_count() == original_count
# Cleanup: close new tab
driver.close()
driver.switch_to.window(driver.window_handles[0])
def test_cart_persists_after_login(self, driver):
"""
Given: Guest user has items in cart
When: User logs in
Then: Cart items are preserved
"""
# Add as guest
products = ProductsPage(driver).open()
products.add_product_to_cart(0)
guest_cart_count = Header(driver).get_cart_count()
# Login
login_page = LoginPage(driver).open()
login_page.login("test@example.com", "password123")
# Check cart
assert Header(driver).get_cart_count() == guest_cart_count
Test Cases: Cart Calculations
# tests/test_cart_calculations.py
class TestCartCalculations:
def test_cart_total_matches_item_sum(self, driver, cart_with_multiple_items):
"""
Given: Cart has multiple items
Then: Total equals sum of all item subtotals
"""
cart = cart_with_multiple_items
expected_total = 0
for i in range(cart.get_item_count()):
item = cart.get_item_by_index(i)
expected_total += item.price * item.quantity
actual_total = cart.get_total()
assert abs(actual_total - expected_total) < 0.01
def test_promo_code_applies_discount(self, driver, cart_with_one_item):
"""
Given: Cart total is $100
When: User applies 10% off promo code
Then: Total shows $90
"""
cart = cart_with_one_item
# Ensure cart has predictable value
original_total = cart.get_total()
cart.apply_promo_code("SAVE10")
# Check discount applied
new_total = cart.get_total()
expected = original_total * 0.9
assert abs(new_total - expected) < 0.01
def test_invalid_promo_code_shows_error(self, driver, cart_with_one_item):
"""
Given: User enters invalid promo code
Then: Error message is displayed
And: Total is unchanged
"""
cart = cart_with_one_item
original_total = cart.get_total()
cart.apply_promo_code("INVALID123")
assert cart.is_visible(cart.PROMO_ERROR)
assert cart.get_total() == original_total
Fixtures for Cart Tests
# tests/conftest.py
import pytest
from pages.products_page import ProductsPage
from pages.cart_page import CartPage
from pages.components.header import Header
@pytest.fixture
def cart_with_one_item(driver):
"""Provide cart with single item"""
products = ProductsPage(driver).open()
products.add_product_to_cart(0)
return Header(driver).go_to_cart()
@pytest.fixture
def cart_with_three_items(driver):
"""Provide cart with three different items"""
products = ProductsPage(driver).open()
products.add_product_to_cart(0)
products.add_product_to_cart(1)
products.add_product_to_cart(2)
return Header(driver).go_to_cart()
@pytest.fixture
def cart_with_multiple_items(driver):
"""Alias for cart_with_three_items"""
return cart_with_three_items(driver)
Edge Cases to Consider
| Scenario | Test Approach |
|---|---|
| Add to cart while not logged in | Verify guest cart works |
| Cart item goes out of stock | Check warning/removal |
| Price changes after adding | Verify updated price shows |
| Network error during add | Test error handling |
| Concurrent cart updates | Two tabs adding items |
Key Takeaways
- Prioritize revenue-critical paths - Add, remove, checkout
- Test calculations precisely - Use small tolerance (0.01) for floating point
- Verify persistence - Refresh, new tab, login
- Use fixtures for setup - DRY test code
- Cover edge cases - Out of stock, invalid input, errors
Next Lesson
The cart leads to Checkout & Payment Testingโthe most critical (and complex) part of e-commerce testing.