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:
- Management API tests — verify catalog, order, and customer data
- Storefront API tests — verify cart and checkout logic
- Theme/Stencil tests — verify rendered HTML and JS behavior
- 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"] == 11Storefront 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_idGraphQL 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) > 0End-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 amountStencil 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=falseBigCommerce 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