BigCommerce Storefront Testing: API Automation and UI Testing Guide

BigCommerce Storefront Testing: API Automation and UI Testing Guide

BigCommerce testing spans the Management API (products, orders, customers), the Storefront API (cart, checkout), and the Stencil theme layer. This guide covers each with Python and JavaScript examples, including complete checkout automation.


BigCommerce Testing Stack

BigCommerce has a different architecture from WooCommerce or Magento:

  • Management API — REST API for products, orders, customers, catalog (server-side)
  • Storefront API — REST/GraphQL for cart, checkout, customer sessions (browser-side)
  • Stencil — the theme framework (Handlebars templates + JS)
  • Custom apps — BigCommerce apps installed via the marketplace

Testing strategy:

  1. Management API tests — verify catalog, order, and customer data
  2. Storefront API tests — verify cart and checkout logic
  3. Theme/Stencil tests — verify rendered HTML and JS behavior
  4. E2E tests — verify complete user journeys via browser

Setting Up API Credentials

# config.py
import os

BC_STORE_HASH = os.environ["BC_STORE_HASH"]      # from store settings
BC_CLIENT_ID = os.environ["BC_CLIENT_ID"]         # from API account
BC_ACCESS_TOKEN = os.environ["BC_ACCESS_TOKEN"]   # from API account

MANAGEMENT_API = f"https://api.bigcommerce.com/stores/{BC_STORE_HASH}/v3"
MANAGEMENT_HEADERS = {
    "X-Auth-Token": BC_ACCESS_TOKEN,
    "Content-Type": "application/json",
    "Accept": "application/json"
}

# Storefront API uses store URL
STOREFRONT_URL = f"https://{os.environ['BC_STORE_DOMAIN']}"

Management API Testing

Product Tests

# tests/test_products.py
import pytest
import requests
from config import MANAGEMENT_API, MANAGEMENT_HEADERS

@pytest.fixture(autouse=True)
def cleanup(request):
    """Track created resources and delete after test."""
    created_ids = []
    yield created_ids
    for product_id in created_ids:
        requests.delete(f"{MANAGEMENT_API}/catalog/products/{product_id}",
                       headers=MANAGEMENT_HEADERS)

def test_create_simple_product(cleanup):
    payload = {
        "name": f"Test Product {pytest.current_test_id}",
        "type": "physical",
        "sku": "TEST-001",
        "price": 29.99,
        "weight": 1.5,
        "categories": [18],  # test category
        "is_visible": True
    }
    
    response = requests.post(
        f"{MANAGEMENT_API}/catalog/products",
        json=payload,
        headers=MANAGEMENT_HEADERS
    )
    
    assert response.status_code == 200
    product = response.json()["data"]
    cleanup.append(product["id"])
    
    assert product["name"] == payload["name"]
    assert product["price"] == "29.9900"
    assert product["is_visible"] is True

def test_update_product_inventory(cleanup):
    # Create product
    response = requests.post(
        f"{MANAGEMENT_API}/catalog/products",
        json={"name": "Inventory Test", "type": "physical", "price": 10.00, "weight": 1.0},
        headers=MANAGEMENT_HEADERS
    )
    product_id = response.json()["data"]["id"]
    cleanup.append(product_id)
    
    # Update inventory
    update = requests.put(
        f"{MANAGEMENT_API}/catalog/products/{product_id}",
        json={"inventory_level": 50, "inventory_tracking": "product"},
        headers=MANAGEMENT_HEADERS
    )
    
    assert update.status_code == 200
    assert update.json()["data"]["inventory_level"] == 50

def test_create_product_variant():
    # Create base product
    product_resp = requests.post(
        f"{MANAGEMENT_API}/catalog/products",
        json={
            "name": "T-Shirt",
            "type": "physical",
            "price": 25.00,
            "weight": 0.5
        },
        headers=MANAGEMENT_HEADERS
    )
    product_id = product_resp.json()["data"]["id"]
    
    # Add size option
    option_resp = requests.post(
        f"{MANAGEMENT_API}/catalog/products/{product_id}/options",
        json={
            "display_name": "Size",
            "type": "radio_buttons",
            "option_values": [
                {"label": "Small", "sort_order": 0, "is_default": True},
                {"label": "Medium", "sort_order": 1},
                {"label": "Large", "sort_order": 2}
            ]
        },
        headers=MANAGEMENT_HEADERS
    )
    
    assert option_resp.status_code == 200
    options = option_resp.json()["data"]
    assert options["display_name"] == "Size"
    assert len(options["option_values"]) == 3
    
    # Cleanup
    requests.delete(f"{MANAGEMENT_API}/catalog/products/{product_id}",
                   headers=MANAGEMENT_HEADERS)

Order Testing

# tests/test_orders.py
import requests
from config import MANAGEMENT_API, MANAGEMENT_HEADERS

def test_create_manual_order():
    response = requests.post(
        f"{MANAGEMENT_API}/orders",
        json={
            "status_id": 1,  # Pending
            "billing_address": {
                "first_name": "Alice",
                "last_name": "Test",
                "email": "alice@test.com",
                "street_1": "123 Test St",
                "city": "Portland",
                "state": "Oregon",
                "zip": "97201",
                "country": "United States",
                "country_iso2": "US"
            },
            "products": [
                {
                    "product_id": 86,  # existing product
                    "quantity": 2,
                    "price_inc_tax": 29.99,
                    "price_ex_tax": 29.99
                }
            ]
        },
        headers=MANAGEMENT_HEADERS
    )
    
    assert response.status_code == 201
    order = response.json()
    assert order["status"] == "Pending"
    assert float(order["total_inc_tax"]) == 59.98
    
    # Cleanup
    requests.put(
        f"{MANAGEMENT_API.replace('/v3', '/v2')}/orders/{order['id']}",
        json={"status_id": 5},  # Cancelled
        headers=MANAGEMENT_HEADERS
    )

def test_update_order_status():
    # Get recent order
    orders = requests.get(
        f"{MANAGEMENT_API.replace('/v3', '/v2')}/orders",
        headers=MANAGEMENT_HEADERS
    ).json()
    
    if not orders:
        pytest.skip("No orders to test with")
    
    order_id = orders[0]["id"]
    
    response = requests.put(
        f"{MANAGEMENT_API.replace('/v3', '/v2')}/orders/{order_id}",
        json={"status_id": 11},  # Awaiting Fulfillment
        headers=MANAGEMENT_HEADERS
    )
    
    assert response.status_code == 200
    assert response.json()["status_id"] == 11

Storefront API Testing

The Storefront API is browser-based and uses customer session tokens.

# tests/test_storefront_cart.py
import requests

STOREFRONT_URL = "https://your-store.mybigcommerce.com"
STOREFRONT_HEADERS = {
    "Content-Type": "application/json",
    # Storefront API doesn't need auth token for cart operations
}

def test_create_cart_and_add_item():
    # Create a cart with an item
    response = requests.post(
        f"{STOREFRONT_URL}/api/storefront/carts",
        json={
            "lineItems": [
                {
                    "productId": 86,  # your test product
                    "quantity": 2
                }
            ]
        },
        headers=STOREFRONT_HEADERS
    )
    
    assert response.status_code == 200
    cart = response.json()
    cart_id = cart["id"]
    
    assert len(cart["lineItems"]["physicalItems"]) == 1
    assert cart["lineItems"]["physicalItems"][0]["quantity"] == 2
    
    return cart_id

def test_apply_coupon_to_cart():
    cart_id = test_create_cart_and_add_item()
    
    response = requests.post(
        f"{STOREFRONT_URL}/api/storefront/carts/{cart_id}/coupons",
        json={"couponCode": "SAVE10"},
        headers=STOREFRONT_HEADERS
    )
    
    assert response.status_code == 200
    cart = response.json()
    
    # Coupon should reduce total
    assert any(c["code"] == "SAVE10" for c in cart.get("coupons", []))
    
def test_cart_persists_across_requests():
    cart_id = test_create_cart_and_add_item()
    
    # Retrieve the cart
    get_response = requests.get(
        f"{STOREFRONT_URL}/api/storefront/carts/{cart_id}",
        headers=STOREFRONT_HEADERS
    )
    
    assert get_response.status_code == 200
    assert get_response.json()["id"] == cart_id

GraphQL Storefront API

BigCommerce's GraphQL API is powerful for storefront queries:

import requests

STOREFRONT_URL = "https://your-store.mybigcommerce.com"
GRAPHQL_TOKEN = "your-storefront-api-token"  # generated in store admin

def graphql_query(query: str, variables: dict = None) -> dict:
    response = requests.post(
        f"{STOREFRONT_URL}/graphql",
        json={"query": query, "variables": variables or {}},
        headers={
            "Authorization": f"Bearer {GRAPHQL_TOKEN}",
            "Content-Type": "application/json"
        }
    )
    return response.json()

def test_get_product_by_id():
    result = graphql_query("""
        query GetProduct($productId: Int!) {
            site {
                product(entityId: $productId) {
                    name
                    prices {
                        price {
                            value
                            currencyCode
                        }
                    }
                    inventory {
                        isInStock
                    }
                }
            }
        }
    """, {"productId": 86})
    
    product = result["data"]["site"]["product"]
    assert product is not None
    assert product["name"]
    assert product["prices"]["price"]["value"] > 0
    assert product["inventory"]["isInStock"] is True

def test_search_products():
    result = graphql_query("""
        query SearchProducts($query: String!) {
            site {
                search {
                    searchProducts(filters: {searchTerm: $query}) {
                        products {
                            edges {
                                node {
                                    name
                                    entityId
                                }
                            }
                        }
                    }
                }
            }
        }
    """, {"query": "widget"})
    
    products = result["data"]["site"]["search"]["searchProducts"]["products"]["edges"]
    assert len(products) > 0

End-to-End Checkout Testing

# tests/e2e/test_bigcommerce_checkout.py
import pytest
from playwright.sync_api import Page, expect

BASE_URL = "https://your-store.mybigcommerce.com"

def test_guest_checkout(page: Page):
    # Navigate to a product
    page.goto(f"{BASE_URL}/test-product/")
    
    # Add to cart
    page.click("[data-button-type='add-cart']")
    expect(page.locator(".previewCartList-item")).to_have_count(1)
    
    # Go to cart
    page.click("[data-cart-preview]")
    page.click("text=Go to cart")
    expect(page.locator(".cart-quantity")).to_be_visible()
    
    # Checkout
    page.click("text=Check Out")
    
    # Fill email for guest
    page.fill("#email", "guest@test.com")
    page.click("text=Continue")
    
    # Fill shipping
    page.fill("#shippingAddress\\.firstName", "Alice")
    page.fill("#shippingAddress\\.lastName", "Test")
    page.fill("#shippingAddress\\.address1", "123 Test Street")
    page.fill("#shippingAddress\\.city", "Portland")
    page.select_option("#shippingAddress\\.stateOrProvinceCode", "OR")
    page.fill("#shippingAddress\\.postalCode", "97201")
    page.select_option("#shippingAddress\\.countryCode", "US")
    page.click("text=Continue")
    
    # Select shipping method
    page.click(".shippingOption >> nth=0")
    page.click("text=Continue")
    
    # Payment — test card
    page.frame_locator("[id*='bigpay']").locator("[name='cardNumber']").fill("4111111111111111")
    page.frame_locator("[id*='bigpay']").locator("[name='expiryDate']").fill("12/28")
    page.frame_locator("[id*='bigpay']").locator("[name='cvv']").fill("123")
    page.frame_locator("[id*='bigpay']").locator("[name='ccName']").fill("Alice Test")
    
    # Place order
    page.click("text=Place Order")
    
    # Confirm
    expect(page.locator("text=Thank you for your purchase!")).to_be_visible(timeout=30000)
    expect(page.locator(".orderConfirmation-order")).to_be_visible()

def test_coupon_applied_in_checkout(page: Page):
    page.goto(f"{BASE_URL}/test-product/")
    page.click("[data-button-type='add-cart']")
    
    page.goto(f"{BASE_URL}/cart.php")
    
    # Apply coupon before checkout
    page.click("text=I have a coupon code")
    page.fill("[name='couponcode']", "SAVE10")
    page.click("text=Apply Coupon")
    
    expect(page.locator(".cart-discount")).to_be_visible()
    # Verify discount is applied (amount depends on products)
    discount_text = page.locator(".cart-discount").text_content()
    assert "-" in discount_text  # Should show negative amount

Stencil Theme Testing

Test your BigCommerce Stencil theme in a headless environment:

// tests/stencil/test-product-card.test.js
import { JSDOM } from 'jsdom';
import { ProductCard } from '../../assets/js/theme/components/product-card.js';

describe('ProductCard', () => {
    let dom;
    let document;
    
    beforeEach(() => {
        dom = new JSDOM(`
            <div class="card" data-product-id="86">
                <div class="card-body">
                    <h4 class="card-title">Test Widget</h4>
                    <div class="card-text--price">$29.99</div>
                    <button class="button--cart" data-product-id="86">Add to Cart</button>
                </div>
            </div>
        `);
        document = dom.window.document;
    });
    
    it('shows product name', () => {
        const title = document.querySelector('.card-title').textContent;
        expect(title).toBe('Test Widget');
    });
    
    it('shows correct price', () => {
        const price = document.querySelector('.card-text--price').textContent;
        expect(price).toBe('$29.99');
    });
    
    it('add to cart button has product ID', () => {
        const btn = document.querySelector('.button--cart');
        expect(btn.dataset.productId).toBe('86');
    });
});

CI Setup

name: BigCommerce Tests

on: [push, pull_request]

jobs:
  api-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: pip install pytest requests
      - name: Run API tests
        env:
          BC_STORE_HASH: ${{ secrets.BC_STORE_HASH }}
          BC_ACCESS_TOKEN: ${{ secrets.BC_ACCESS_TOKEN }}
          BC_STORE_DOMAIN: ${{ secrets.BC_STORE_DOMAIN }}
        run: pytest tests/test_products.py tests/test_orders.py -v
  
  e2e-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: pip install pytest playwright
      - run: playwright install chromium
      - name: Run E2E tests
        env:
          BC_STORE_DOMAIN: ${{ secrets.BC_STORE_DOMAIN }}
        run: pytest tests/e2e/ -v --headed=false

BigCommerce Test Card Numbers

For test/sandbox mode:

Card Number Result
Visa 4111 1111 1111 1111 Success
Mastercard 5500 0000 0000 0004 Success
Amex 3782 822463 10005 Success
Decline 4000 0000 0000 0002 Declined
Insufficient funds 4000 0000 0000 9995 Insufficient funds

Always use a sandbox/test store for automated testing — never use production credentials.


Next Steps

  • Set up a test store — BigCommerce offers sandbox stores for development
  • Use the Management API for data setup/teardown instead of UI manipulation
  • Explore e-commerce regression testing for a release checklist
  • Monitor with HelpMeTest — schedule checkout tests every 5 minutes and alert when they fail

Read more