Get Started
StateSet One Guides
- Orders Management Quickstart
- Order settlements
- Wholesale Quickstart
- Returns Quickstart
- Warranties Quickstart
- Subscriptions Quickstart
- Manufacturing and Production Quickstart
- Warehouse Quickstart
- COGS Quickstart Guide
- Supplier Quickstart
- Inventory Management Quickstart
- Error Handling Best Practices
- API Rate Limiting & Optimization
- Performance Optimization Guide
- Comprehensive Testing Guide
StateSet ResponseCX Guides
- Getting Started with ResponseCX
- Agent Training Guide
- Agent Objectives, Goals, Metrics & Rewards Guide
- Multi-Agent System Architectures
- Reinforcement Learning Platform Overview
- GRPO Agent Framework
- Knowledge Base Quickstart
- RAG Quickstart
- Agents Quickstart
- Agent Attributes & Personality
- Agent Rules Quickstart
- Examples Quickstart
- Evaluations
- Agent Schedules & Automation
- Agent Functions Quickstart
- Shopify Product Quickstart
- Gorgias Ticket Quickstart
- Vision Quickstart
- Voice AI Quickstart
- Synthetic Data Studio
- StateSet Synthetic Data Studio Architecture Guide
StateSet Commerce Network Guides
StateSet ReSponse API Documentation
StateSet One Guides
Comprehensive Testing Guide
Complete guide to testing StateSet API integrations including unit tests, integration tests, and end-to-end testing strategies
Comprehensive Testing Guide
Testing is crucial for building reliable StateSet integrations. This guide covers testing strategies from unit tests to end-to-end scenarios, helping you build confidence in your integration.
Testing Overview
Unit Testing
Test individual functions and components in isolation
Integration Testing
Test interactions between your code and StateSet APIs
End-to-End Testing
Test complete workflows from start to finish
Test Environment Setup
Testing Pyramid Strategy
Environment Configuration
Copy
Ask AI
// jest.config.js
module.exports = {
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
testMatch: [
'**/__tests__/**/*.test.js',
'**/*.test.js'
],
collectCoverageFrom: [
'src/**/*.js',
'!src/**/*.test.js',
'!src/index.js'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
testTimeout: 30000
};
// tests/setup.js
import dotenv from 'dotenv';
import { jest } from '@jest/globals';
// Load test environment variables
dotenv.config({ path: '.env.test' });
// Global test setup
beforeAll(() => {
// Mock console methods in tests
global.console = {
...console,
log: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
};
});
afterAll(() => {
// Cleanup after all tests
jest.restoreAllMocks();
});
Copy
Ask AI
// jest.config.js
module.exports = {
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
testMatch: [
'**/__tests__/**/*.test.js',
'**/*.test.js'
],
collectCoverageFrom: [
'src/**/*.js',
'!src/**/*.test.js',
'!src/index.js'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
testTimeout: 30000
};
// tests/setup.js
import dotenv from 'dotenv';
import { jest } from '@jest/globals';
// Load test environment variables
dotenv.config({ path: '.env.test' });
// Global test setup
beforeAll(() => {
// Mock console methods in tests
global.console = {
...console,
log: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
};
});
afterAll(() => {
// Cleanup after all tests
jest.restoreAllMocks();
});
Copy
Ask AI
# pytest.ini
[tool:pytest]
minversion = 6.0
addopts =
-ra
--strict-markers
--strict-config
--cov=src
--cov-report=term-missing
--cov-report=html
--cov-fail-under=80
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
# conftest.py
import pytest
import os
from unittest.mock import Mock
from stateset import StateSetClient
@pytest.fixture(scope="session")
def test_config():
"""Test configuration fixture."""
return {
'api_key': os.getenv('STATESET_TEST_API_KEY', 'sk_test_fake_key'),
'base_url': 'https://api.sandbox.stateset.com',
'timeout': 30
}
@pytest.fixture
def mock_client():
"""Mock StateSet client for unit tests."""
return Mock(spec=StateSetClient)
@pytest.fixture
def real_client(test_config):
"""Real StateSet client for integration tests."""
return StateSetClient(**test_config)
@pytest.fixture(autouse=True)
def cleanup_test_data():
"""Cleanup test data after each test."""
yield
# Add cleanup logic here
pass
Copy
Ask AI
# spec/spec_helper.rb
require 'bundler/setup'
require 'stateset'
require 'webmock/rspec'
require 'vcr'
# Configure VCR for HTTP recording
VCR.configure do |config|
config.cassette_library_dir = 'spec/vcr_cassettes'
config.hook_into :webmock
config.configure_rspec_metadata!
config.filter_sensitive_data('<STATESET_API_KEY>') { ENV['STATESET_API_KEY'] }
end
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
# Test environment setup
config.before(:suite) do
Stateset.configure do |stateset_config|
stateset_config.api_key = ENV['STATESET_TEST_API_KEY'] || 'sk_test_fake'
stateset_config.environment = 'test'
end
end
end
Unit Testing
Testing Individual Functions
Copy
Ask AI
// src/orderProcessor.js
import { StateSetClient } from 'stateset-node';
import winston from 'winston';
export class OrderProcessor {
constructor(client, logger = winston.createLogger()) {
this.client = client;
this.logger = logger;
}
async processOrder(orderData) {
try {
// Validate order data
const validation = this.validateOrder(orderData);
if (!validation.isValid) {
throw new Error(`Validation failed: ${validation.errors.join(', ')}`);
}
// Create order
const order = await this.client.orders.create(orderData);
this.logger.info('Order processed successfully', { orderId: order.id });
return order;
} catch (error) {
this.logger.error('Order processing failed', { error: error.message });
throw error;
}
}
validateOrder(orderData) {
const errors = [];
if (!orderData.customer_email) {
errors.push('Customer email is required');
}
if (!orderData.items || orderData.items.length === 0) {
errors.push('Order must contain at least one item');
}
if (orderData.items) {
orderData.items.forEach((item, index) => {
if (!item.sku) errors.push(`Item ${index}: SKU is required`);
if (!item.quantity || item.quantity <= 0) {
errors.push(`Item ${index}: Quantity must be positive`);
}
});
}
return {
isValid: errors.length === 0,
errors
};
}
}
// __tests__/orderProcessor.test.js
import { OrderProcessor } from '../src/orderProcessor.js';
import { jest } from '@jest/globals';
describe('OrderProcessor', () => {
let orderProcessor;
let mockClient;
let mockLogger;
beforeEach(() => {
mockClient = {
orders: {
create: jest.fn()
}
};
mockLogger = {
info: jest.fn(),
error: jest.fn()
};
orderProcessor = new OrderProcessor(mockClient, mockLogger);
});
describe('validateOrder', () => {
it('should return valid for correct order data', () => {
const orderData = {
customer_email: 'test@example.com',
items: [
{ sku: 'ITEM-001', quantity: 2 }
]
};
const result = orderProcessor.validateOrder(orderData);
expect(result.isValid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it('should return invalid for missing email', () => {
const orderData = {
items: [{ sku: 'ITEM-001', quantity: 2 }]
};
const result = orderProcessor.validateOrder(orderData);
expect(result.isValid).toBe(false);
expect(result.errors).toContain('Customer email is required');
});
it('should return invalid for empty items', () => {
const orderData = {
customer_email: 'test@example.com',
items: []
};
const result = orderProcessor.validateOrder(orderData);
expect(result.isValid).toBe(false);
expect(result.errors).toContain('Order must contain at least one item');
});
it('should validate individual items', () => {
const orderData = {
customer_email: 'test@example.com',
items: [
{ sku: 'ITEM-001', quantity: 2 },
{ quantity: 1 }, // Missing SKU
{ sku: 'ITEM-003', quantity: -1 } // Invalid quantity
]
};
const result = orderProcessor.validateOrder(orderData);
expect(result.isValid).toBe(false);
expect(result.errors).toContain('Item 1: SKU is required');
expect(result.errors).toContain('Item 2: Quantity must be positive');
});
});
describe('processOrder', () => {
it('should create order successfully', async () => {
const orderData = {
customer_email: 'test@example.com',
items: [{ sku: 'ITEM-001', quantity: 2 }]
};
const expectedOrder = { id: 'order_123', ...orderData };
mockClient.orders.create.mockResolvedValue(expectedOrder);
const result = await orderProcessor.processOrder(orderData);
expect(mockClient.orders.create).toHaveBeenCalledWith(orderData);
expect(mockLogger.info).toHaveBeenCalledWith(
'Order processed successfully',
{ orderId: 'order_123' }
);
expect(result).toEqual(expectedOrder);
});
it('should throw error for invalid order data', async () => {
const orderData = { items: [] }; // Invalid data
await expect(orderProcessor.processOrder(orderData)).rejects.toThrow(
'Validation failed'
);
expect(mockClient.orders.create).not.toHaveBeenCalled();
expect(mockLogger.error).toHaveBeenCalled();
});
it('should handle API errors', async () => {
const orderData = {
customer_email: 'test@example.com',
items: [{ sku: 'ITEM-001', quantity: 2 }]
};
const apiError = new Error('API Error');
mockClient.orders.create.mockRejectedValue(apiError);
await expect(orderProcessor.processOrder(orderData)).rejects.toThrow('API Error');
expect(mockLogger.error).toHaveBeenCalledWith(
'Order processing failed',
{ error: 'API Error' }
);
});
});
});
Copy
Ask AI
// src/orderProcessor.js
import { StateSetClient } from 'stateset-node';
import winston from 'winston';
export class OrderProcessor {
constructor(client, logger = winston.createLogger()) {
this.client = client;
this.logger = logger;
}
async processOrder(orderData) {
try {
// Validate order data
const validation = this.validateOrder(orderData);
if (!validation.isValid) {
throw new Error(`Validation failed: ${validation.errors.join(', ')}`);
}
// Create order
const order = await this.client.orders.create(orderData);
this.logger.info('Order processed successfully', { orderId: order.id });
return order;
} catch (error) {
this.logger.error('Order processing failed', { error: error.message });
throw error;
}
}
validateOrder(orderData) {
const errors = [];
if (!orderData.customer_email) {
errors.push('Customer email is required');
}
if (!orderData.items || orderData.items.length === 0) {
errors.push('Order must contain at least one item');
}
if (orderData.items) {
orderData.items.forEach((item, index) => {
if (!item.sku) errors.push(`Item ${index}: SKU is required`);
if (!item.quantity || item.quantity <= 0) {
errors.push(`Item ${index}: Quantity must be positive`);
}
});
}
return {
isValid: errors.length === 0,
errors
};
}
}
// __tests__/orderProcessor.test.js
import { OrderProcessor } from '../src/orderProcessor.js';
import { jest } from '@jest/globals';
describe('OrderProcessor', () => {
let orderProcessor;
let mockClient;
let mockLogger;
beforeEach(() => {
mockClient = {
orders: {
create: jest.fn()
}
};
mockLogger = {
info: jest.fn(),
error: jest.fn()
};
orderProcessor = new OrderProcessor(mockClient, mockLogger);
});
describe('validateOrder', () => {
it('should return valid for correct order data', () => {
const orderData = {
customer_email: 'test@example.com',
items: [
{ sku: 'ITEM-001', quantity: 2 }
]
};
const result = orderProcessor.validateOrder(orderData);
expect(result.isValid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it('should return invalid for missing email', () => {
const orderData = {
items: [{ sku: 'ITEM-001', quantity: 2 }]
};
const result = orderProcessor.validateOrder(orderData);
expect(result.isValid).toBe(false);
expect(result.errors).toContain('Customer email is required');
});
it('should return invalid for empty items', () => {
const orderData = {
customer_email: 'test@example.com',
items: []
};
const result = orderProcessor.validateOrder(orderData);
expect(result.isValid).toBe(false);
expect(result.errors).toContain('Order must contain at least one item');
});
it('should validate individual items', () => {
const orderData = {
customer_email: 'test@example.com',
items: [
{ sku: 'ITEM-001', quantity: 2 },
{ quantity: 1 }, // Missing SKU
{ sku: 'ITEM-003', quantity: -1 } // Invalid quantity
]
};
const result = orderProcessor.validateOrder(orderData);
expect(result.isValid).toBe(false);
expect(result.errors).toContain('Item 1: SKU is required');
expect(result.errors).toContain('Item 2: Quantity must be positive');
});
});
describe('processOrder', () => {
it('should create order successfully', async () => {
const orderData = {
customer_email: 'test@example.com',
items: [{ sku: 'ITEM-001', quantity: 2 }]
};
const expectedOrder = { id: 'order_123', ...orderData };
mockClient.orders.create.mockResolvedValue(expectedOrder);
const result = await orderProcessor.processOrder(orderData);
expect(mockClient.orders.create).toHaveBeenCalledWith(orderData);
expect(mockLogger.info).toHaveBeenCalledWith(
'Order processed successfully',
{ orderId: 'order_123' }
);
expect(result).toEqual(expectedOrder);
});
it('should throw error for invalid order data', async () => {
const orderData = { items: [] }; // Invalid data
await expect(orderProcessor.processOrder(orderData)).rejects.toThrow(
'Validation failed'
);
expect(mockClient.orders.create).not.toHaveBeenCalled();
expect(mockLogger.error).toHaveBeenCalled();
});
it('should handle API errors', async () => {
const orderData = {
customer_email: 'test@example.com',
items: [{ sku: 'ITEM-001', quantity: 2 }]
};
const apiError = new Error('API Error');
mockClient.orders.create.mockRejectedValue(apiError);
await expect(orderProcessor.processOrder(orderData)).rejects.toThrow('API Error');
expect(mockLogger.error).toHaveBeenCalledWith(
'Order processing failed',
{ error: 'API Error' }
);
});
});
});
Copy
Ask AI
# src/order_processor.py
import logging
from typing import Dict, List, Any
from stateset import StateSetClient
class OrderProcessor:
def __init__(self, client: StateSetClient, logger=None):
self.client = client
self.logger = logger or logging.getLogger(__name__)
async def process_order(self, order_data: Dict[str, Any]) -> Dict[str, Any]:
try:
# Validate order data
validation = self.validate_order(order_data)
if not validation['is_valid']:
raise ValueError(f"Validation failed: {', '.join(validation['errors'])}")
# Create order
order = await self.client.orders.create(order_data)
self.logger.info(f'Order processed successfully: {order["id"]}')
return order
except Exception as error:
self.logger.error(f'Order processing failed: {str(error)}')
raise
def validate_order(self, order_data: Dict[str, Any]) -> Dict[str, Any]:
errors = []
if not order_data.get('customer_email'):
errors.append('Customer email is required')
items = order_data.get('items', [])
if not items:
errors.append('Order must contain at least one item')
for index, item in enumerate(items):
if not item.get('sku'):
errors.append(f'Item {index}: SKU is required')
if not item.get('quantity') or item.get('quantity') <= 0:
errors.append(f'Item {index}: Quantity must be positive')
return {
'is_valid': len(errors) == 0,
'errors': errors
}
# tests/test_order_processor.py
import pytest
from unittest.mock import AsyncMock, Mock
from src.order_processor import OrderProcessor
class TestOrderProcessor:
@pytest.fixture
def mock_client(self):
client = Mock()
client.orders = Mock()
client.orders.create = AsyncMock()
return client
@pytest.fixture
def mock_logger(self):
return Mock()
@pytest.fixture
def order_processor(self, mock_client, mock_logger):
return OrderProcessor(mock_client, mock_logger)
def test_validate_order_valid_data(self, order_processor):
order_data = {
'customer_email': 'test@example.com',
'items': [{'sku': 'ITEM-001', 'quantity': 2}]
}
result = order_processor.validate_order(order_data)
assert result['is_valid'] is True
assert len(result['errors']) == 0
def test_validate_order_missing_email(self, order_processor):
order_data = {
'items': [{'sku': 'ITEM-001', 'quantity': 2}]
}
result = order_processor.validate_order(order_data)
assert result['is_valid'] is False
assert 'Customer email is required' in result['errors']
def test_validate_order_empty_items(self, order_processor):
order_data = {
'customer_email': 'test@example.com',
'items': []
}
result = order_processor.validate_order(order_data)
assert result['is_valid'] is False
assert 'Order must contain at least one item' in result['errors']
def test_validate_order_invalid_items(self, order_processor):
order_data = {
'customer_email': 'test@example.com',
'items': [
{'sku': 'ITEM-001', 'quantity': 2},
{'quantity': 1}, # Missing SKU
{'sku': 'ITEM-003', 'quantity': -1} # Invalid quantity
]
}
result = order_processor.validate_order(order_data)
assert result['is_valid'] is False
assert 'Item 1: SKU is required' in result['errors']
assert 'Item 2: Quantity must be positive' in result['errors']
@pytest.mark.asyncio
async def test_process_order_success(self, order_processor, mock_client, mock_logger):
order_data = {
'customer_email': 'test@example.com',
'items': [{'sku': 'ITEM-001', 'quantity': 2}]
}
expected_order = {'id': 'order_123', **order_data}
mock_client.orders.create.return_value = expected_order
result = await order_processor.process_order(order_data)
mock_client.orders.create.assert_called_once_with(order_data)
mock_logger.info.assert_called_once_with('Order processed successfully: order_123')
assert result == expected_order
@pytest.mark.asyncio
async def test_process_order_validation_error(self, order_processor, mock_client):
order_data = {'items': []} # Invalid data
with pytest.raises(ValueError, match='Validation failed'):
await order_processor.process_order(order_data)
mock_client.orders.create.assert_not_called()
@pytest.mark.asyncio
async def test_process_order_api_error(self, order_processor, mock_client, mock_logger):
order_data = {
'customer_email': 'test@example.com',
'items': [{'sku': 'ITEM-001', 'quantity': 2}]
}
mock_client.orders.create.side_effect = Exception('API Error')
with pytest.raises(Exception, match='API Error'):
await order_processor.process_order(order_data)
mock_logger.error.assert_called_once_with('Order processing failed: API Error')
Integration Testing
Testing API Interactions
Copy
Ask AI
// __tests__/integration/orders.integration.test.js
import { StateSetClient } from 'stateset-node';
import { OrderProcessor } from '../../src/orderProcessor.js';
describe('Orders Integration Tests', () => {
let client;
let orderProcessor;
let createdOrderIds = [];
beforeAll(() => {
client = new StateSetClient({
apiKey: process.env.STATESET_TEST_API_KEY,
environment: 'sandbox'
});
orderProcessor = new OrderProcessor(client);
});
afterAll(async () => {
// Cleanup created orders
for (const orderId of createdOrderIds) {
try {
await client.orders.delete(orderId);
} catch (error) {
console.warn(`Failed to cleanup order ${orderId}:`, error.message);
}
}
});
it('should create and retrieve an order', async () => {
const orderData = {
customer_email: 'integration-test@example.com',
items: [
{
sku: 'TEST-ITEM-001',
quantity: 2,
price: 1999
}
],
shipping_address: {
line1: '123 Test Street',
city: 'Test City',
state: 'CA',
postal_code: '90210',
country: 'US'
}
};
// Create order
const createdOrder = await orderProcessor.processOrder(orderData);
createdOrderIds.push(createdOrder.id);
expect(createdOrder.id).toBeDefined();
expect(createdOrder.customer_email).toBe(orderData.customer_email);
expect(createdOrder.status).toBe('pending');
// Retrieve order
const retrievedOrder = await client.orders.get(createdOrder.id);
expect(retrievedOrder.id).toBe(createdOrder.id);
expect(retrievedOrder.customer_email).toBe(orderData.customer_email);
});
it('should handle order status updates', async () => {
const orderData = {
customer_email: 'status-test@example.com',
items: [{ sku: 'TEST-ITEM-002', quantity: 1, price: 999 }],
shipping_address: {
line1: '456 Status Street',
city: 'Status City',
state: 'NY',
postal_code: '10001',
country: 'US'
}
};
const order = await orderProcessor.processOrder(orderData);
createdOrderIds.push(order.id);
// Update order status
const updatedOrder = await client.orders.update(order.id, {
status: 'confirmed'
});
expect(updatedOrder.status).toBe('confirmed');
// Verify status change
const retrievedOrder = await client.orders.get(order.id);
expect(retrievedOrder.status).toBe('confirmed');
});
it('should handle order cancellation', async () => {
const orderData = {
customer_email: 'cancel-test@example.com',
items: [{ sku: 'TEST-ITEM-003', quantity: 1, price: 1500 }],
shipping_address: {
line1: '789 Cancel Street',
city: 'Cancel City',
state: 'TX',
postal_code: '75001',
country: 'US'
}
};
const order = await orderProcessor.processOrder(orderData);
createdOrderIds.push(order.id);
// Cancel order
const cancelledOrder = await client.orders.cancel(order.id, {
reason: 'Customer requested cancellation'
});
expect(cancelledOrder.status).toBe('cancelled');
expect(cancelledOrder.cancellation_reason).toBe('Customer requested cancellation');
});
it('should handle API errors gracefully', async () => {
const invalidOrderData = {
customer_email: 'invalid-test@example.com',
items: [
{
sku: 'INVALID-SKU-THAT-DOES-NOT-EXIST',
quantity: 1,
price: 999
}
]
};
await expect(orderProcessor.processOrder(invalidOrderData)).rejects.toThrow();
});
it('should handle rate limiting', async () => {
const requests = [];
// Make multiple concurrent requests to test rate limiting
for (let i = 0; i < 10; i++) {
requests.push(
client.orders.list({ limit: 1 })
);
}
const results = await Promise.allSettled(requests);
// At least some requests should succeed
const successful = results.filter(r => r.status === 'fulfilled');
expect(successful.length).toBeGreaterThan(0);
// Check if any were rate limited
const rateLimited = results.filter(
r => r.status === 'rejected' && r.reason.status === 429
);
if (rateLimited.length > 0) {
console.log(`${rateLimited.length} requests were rate limited (expected behavior)`);
}
});
});
Copy
Ask AI
// __tests__/integration/orders.integration.test.js
import { StateSetClient } from 'stateset-node';
import { OrderProcessor } from '../../src/orderProcessor.js';
describe('Orders Integration Tests', () => {
let client;
let orderProcessor;
let createdOrderIds = [];
beforeAll(() => {
client = new StateSetClient({
apiKey: process.env.STATESET_TEST_API_KEY,
environment: 'sandbox'
});
orderProcessor = new OrderProcessor(client);
});
afterAll(async () => {
// Cleanup created orders
for (const orderId of createdOrderIds) {
try {
await client.orders.delete(orderId);
} catch (error) {
console.warn(`Failed to cleanup order ${orderId}:`, error.message);
}
}
});
it('should create and retrieve an order', async () => {
const orderData = {
customer_email: 'integration-test@example.com',
items: [
{
sku: 'TEST-ITEM-001',
quantity: 2,
price: 1999
}
],
shipping_address: {
line1: '123 Test Street',
city: 'Test City',
state: 'CA',
postal_code: '90210',
country: 'US'
}
};
// Create order
const createdOrder = await orderProcessor.processOrder(orderData);
createdOrderIds.push(createdOrder.id);
expect(createdOrder.id).toBeDefined();
expect(createdOrder.customer_email).toBe(orderData.customer_email);
expect(createdOrder.status).toBe('pending');
// Retrieve order
const retrievedOrder = await client.orders.get(createdOrder.id);
expect(retrievedOrder.id).toBe(createdOrder.id);
expect(retrievedOrder.customer_email).toBe(orderData.customer_email);
});
it('should handle order status updates', async () => {
const orderData = {
customer_email: 'status-test@example.com',
items: [{ sku: 'TEST-ITEM-002', quantity: 1, price: 999 }],
shipping_address: {
line1: '456 Status Street',
city: 'Status City',
state: 'NY',
postal_code: '10001',
country: 'US'
}
};
const order = await orderProcessor.processOrder(orderData);
createdOrderIds.push(order.id);
// Update order status
const updatedOrder = await client.orders.update(order.id, {
status: 'confirmed'
});
expect(updatedOrder.status).toBe('confirmed');
// Verify status change
const retrievedOrder = await client.orders.get(order.id);
expect(retrievedOrder.status).toBe('confirmed');
});
it('should handle order cancellation', async () => {
const orderData = {
customer_email: 'cancel-test@example.com',
items: [{ sku: 'TEST-ITEM-003', quantity: 1, price: 1500 }],
shipping_address: {
line1: '789 Cancel Street',
city: 'Cancel City',
state: 'TX',
postal_code: '75001',
country: 'US'
}
};
const order = await orderProcessor.processOrder(orderData);
createdOrderIds.push(order.id);
// Cancel order
const cancelledOrder = await client.orders.cancel(order.id, {
reason: 'Customer requested cancellation'
});
expect(cancelledOrder.status).toBe('cancelled');
expect(cancelledOrder.cancellation_reason).toBe('Customer requested cancellation');
});
it('should handle API errors gracefully', async () => {
const invalidOrderData = {
customer_email: 'invalid-test@example.com',
items: [
{
sku: 'INVALID-SKU-THAT-DOES-NOT-EXIST',
quantity: 1,
price: 999
}
]
};
await expect(orderProcessor.processOrder(invalidOrderData)).rejects.toThrow();
});
it('should handle rate limiting', async () => {
const requests = [];
// Make multiple concurrent requests to test rate limiting
for (let i = 0; i < 10; i++) {
requests.push(
client.orders.list({ limit: 1 })
);
}
const results = await Promise.allSettled(requests);
// At least some requests should succeed
const successful = results.filter(r => r.status === 'fulfilled');
expect(successful.length).toBeGreaterThan(0);
// Check if any were rate limited
const rateLimited = results.filter(
r => r.status === 'rejected' && r.reason.status === 429
);
if (rateLimited.length > 0) {
console.log(`${rateLimited.length} requests were rate limited (expected behavior)`);
}
});
});
Copy
Ask AI
# tests/integration/test_orders_integration.py
import pytest
import asyncio
from stateset import StateSetClient
from src.order_processor import OrderProcessor
@pytest.mark.integration
class TestOrdersIntegration:
@pytest.fixture(scope="class")
async def client(self):
return StateSetClient(
api_key=os.getenv('STATESET_TEST_API_KEY'),
environment='sandbox'
)
@pytest.fixture(scope="class")
async def order_processor(self, client):
return OrderProcessor(client)
@pytest.fixture
def order_data(self):
return {
'customer_email': 'integration-test@example.com',
'items': [
{
'sku': 'TEST-ITEM-001',
'quantity': 2,
'price': 1999
}
],
'shipping_address': {
'line1': '123 Test Street',
'city': 'Test City',
'state': 'CA',
'postal_code': '90210',
'country': 'US'
}
}
@pytest.mark.asyncio
async def test_create_and_retrieve_order(self, order_processor, client, order_data):
# Create order
created_order = await order_processor.process_order(order_data)
assert created_order['id'] is not None
assert created_order['customer_email'] == order_data['customer_email']
assert created_order['status'] == 'pending'
# Retrieve order
retrieved_order = await client.orders.get(created_order['id'])
assert retrieved_order['id'] == created_order['id']
assert retrieved_order['customer_email'] == order_data['customer_email']
# Cleanup
await client.orders.delete(created_order['id'])
@pytest.mark.asyncio
async def test_order_status_updates(self, order_processor, client, order_data):
order_data['customer_email'] = 'status-test@example.com'
order = await order_processor.process_order(order_data)
# Update order status
updated_order = await client.orders.update(order['id'], {
'status': 'confirmed'
})
assert updated_order['status'] == 'confirmed'
# Verify status change
retrieved_order = await client.orders.get(order['id'])
assert retrieved_order['status'] == 'confirmed'
# Cleanup
await client.orders.delete(order['id'])
@pytest.mark.asyncio
async def test_rate_limiting_handling(self, client):
# Make multiple concurrent requests
tasks = [client.orders.list(limit=1) for _ in range(10)]
results = await asyncio.gather(*tasks, return_exceptions=True)
# At least some should succeed
successful = [r for r in results if not isinstance(r, Exception)]
assert len(successful) > 0
# Check for rate limiting
rate_limited = [r for r in results if isinstance(r, Exception) and getattr(r, 'status', None) == 429]
if rate_limited:
print(f"{len(rate_limited)} requests were rate limited (expected)")
End-to-End Testing
Complete Workflow Testing
Copy
Ask AI
// tests/e2e/order-workflow.e2e.test.js
import { test, expect } from '@playwright/test';
import { StateSetClient } from 'stateset-node';
test.describe('Complete Order Workflow', () => {
let client;
let testOrderId;
test.beforeAll(async () => {
client = new StateSetClient({
apiKey: process.env.STATESET_TEST_API_KEY,
environment: 'sandbox'
});
});
test.afterAll(async () => {
// Cleanup
if (testOrderId) {
try {
await client.orders.delete(testOrderId);
} catch (error) {
console.warn('Failed to cleanup test order:', error.message);
}
}
});
test('should complete full order lifecycle', async ({ page }) => {
// 1. Create order via API
const orderData = {
customer_email: 'e2e-test@example.com',
items: [
{
sku: 'E2E-TEST-ITEM',
quantity: 1,
price: 2999,
name: 'E2E Test Product'
}
],
shipping_address: {
line1: '123 E2E Street',
city: 'Test City',
state: 'CA',
postal_code: '90210',
country: 'US'
}
};
const order = await client.orders.create(orderData);
testOrderId = order.id;
expect(order.status).toBe('pending');
// 2. Navigate to order management dashboard
await page.goto(`${process.env.DASHBOARD_URL}/orders/${order.id}`);
// 3. Verify order details in UI
await expect(page.locator('[data-testid="order-id"]')).toContainText(order.id);
await expect(page.locator('[data-testid="customer-email"]')).toContainText('e2e-test@example.com');
await expect(page.locator('[data-testid="order-status"]')).toContainText('pending');
// 4. Process order through UI
await page.click('[data-testid="confirm-order-btn"]');
await page.waitForSelector('[data-testid="order-status"]:has-text("confirmed")');
// 5. Verify status change via API
const updatedOrder = await client.orders.get(order.id);
expect(updatedOrder.status).toBe('confirmed');
// 6. Ship order
await page.click('[data-testid="ship-order-btn"]');
await page.fill('[data-testid="tracking-number"]', 'TEST-TRACKING-123');
await page.click('[data-testid="confirm-shipment-btn"]');
await page.waitForSelector('[data-testid="order-status"]:has-text("shipped")');
// 7. Verify final status
const shippedOrder = await client.orders.get(order.id);
expect(shippedOrder.status).toBe('shipped');
expect(shippedOrder.tracking_number).toBe('TEST-TRACKING-123');
// 8. Check customer notification
const notifications = await client.notifications.list({
order_id: order.id,
type: 'shipment'
});
expect(notifications.data.length).toBeGreaterThan(0);
expect(notifications.data[0].recipient_email).toBe('e2e-test@example.com');
});
test('should handle order cancellation workflow', async ({ page }) => {
// Create order
const orderData = {
customer_email: 'cancel-e2e-test@example.com',
items: [{ sku: 'CANCEL-TEST-ITEM', quantity: 1, price: 1999 }],
shipping_address: {
line1: '456 Cancel Street',
city: 'Cancel City',
state: 'NY',
postal_code: '10001',
country: 'US'
}
};
const order = await client.orders.create(orderData);
testOrderId = order.id;
// Navigate to order
await page.goto(`${process.env.DASHBOARD_URL}/orders/${order.id}`);
// Cancel order through UI
await page.click('[data-testid="cancel-order-btn"]');
await page.fill('[data-testid="cancellation-reason"]', 'Customer requested cancellation');
await page.click('[data-testid="confirm-cancellation-btn"]');
// Wait for status update
await page.waitForSelector('[data-testid="order-status"]:has-text("cancelled")');
// Verify via API
const cancelledOrder = await client.orders.get(order.id);
expect(cancelledOrder.status).toBe('cancelled');
expect(cancelledOrder.cancellation_reason).toBe('Customer requested cancellation');
});
});
Copy
Ask AI
// tests/e2e/order-workflow.e2e.test.js
import { test, expect } from '@playwright/test';
import { StateSetClient } from 'stateset-node';
test.describe('Complete Order Workflow', () => {
let client;
let testOrderId;
test.beforeAll(async () => {
client = new StateSetClient({
apiKey: process.env.STATESET_TEST_API_KEY,
environment: 'sandbox'
});
});
test.afterAll(async () => {
// Cleanup
if (testOrderId) {
try {
await client.orders.delete(testOrderId);
} catch (error) {
console.warn('Failed to cleanup test order:', error.message);
}
}
});
test('should complete full order lifecycle', async ({ page }) => {
// 1. Create order via API
const orderData = {
customer_email: 'e2e-test@example.com',
items: [
{
sku: 'E2E-TEST-ITEM',
quantity: 1,
price: 2999,
name: 'E2E Test Product'
}
],
shipping_address: {
line1: '123 E2E Street',
city: 'Test City',
state: 'CA',
postal_code: '90210',
country: 'US'
}
};
const order = await client.orders.create(orderData);
testOrderId = order.id;
expect(order.status).toBe('pending');
// 2. Navigate to order management dashboard
await page.goto(`${process.env.DASHBOARD_URL}/orders/${order.id}`);
// 3. Verify order details in UI
await expect(page.locator('[data-testid="order-id"]')).toContainText(order.id);
await expect(page.locator('[data-testid="customer-email"]')).toContainText('e2e-test@example.com');
await expect(page.locator('[data-testid="order-status"]')).toContainText('pending');
// 4. Process order through UI
await page.click('[data-testid="confirm-order-btn"]');
await page.waitForSelector('[data-testid="order-status"]:has-text("confirmed")');
// 5. Verify status change via API
const updatedOrder = await client.orders.get(order.id);
expect(updatedOrder.status).toBe('confirmed');
// 6. Ship order
await page.click('[data-testid="ship-order-btn"]');
await page.fill('[data-testid="tracking-number"]', 'TEST-TRACKING-123');
await page.click('[data-testid="confirm-shipment-btn"]');
await page.waitForSelector('[data-testid="order-status"]:has-text("shipped")');
// 7. Verify final status
const shippedOrder = await client.orders.get(order.id);
expect(shippedOrder.status).toBe('shipped');
expect(shippedOrder.tracking_number).toBe('TEST-TRACKING-123');
// 8. Check customer notification
const notifications = await client.notifications.list({
order_id: order.id,
type: 'shipment'
});
expect(notifications.data.length).toBeGreaterThan(0);
expect(notifications.data[0].recipient_email).toBe('e2e-test@example.com');
});
test('should handle order cancellation workflow', async ({ page }) => {
// Create order
const orderData = {
customer_email: 'cancel-e2e-test@example.com',
items: [{ sku: 'CANCEL-TEST-ITEM', quantity: 1, price: 1999 }],
shipping_address: {
line1: '456 Cancel Street',
city: 'Cancel City',
state: 'NY',
postal_code: '10001',
country: 'US'
}
};
const order = await client.orders.create(orderData);
testOrderId = order.id;
// Navigate to order
await page.goto(`${process.env.DASHBOARD_URL}/orders/${order.id}`);
// Cancel order through UI
await page.click('[data-testid="cancel-order-btn"]');
await page.fill('[data-testid="cancellation-reason"]', 'Customer requested cancellation');
await page.click('[data-testid="confirm-cancellation-btn"]');
// Wait for status update
await page.waitForSelector('[data-testid="order-status"]:has-text("cancelled")');
// Verify via API
const cancelledOrder = await client.orders.get(order.id);
expect(cancelledOrder.status).toBe('cancelled');
expect(cancelledOrder.cancellation_reason).toBe('Customer requested cancellation');
});
});
Copy
Ask AI
// cypress/e2e/order-management.cy.js
describe('Order Management E2E', () => {
let testOrderId;
beforeEach(() => {
// Login to dashboard
cy.login();
});
afterEach(() => {
// Cleanup test orders
if (testOrderId) {
cy.cleanupOrder(testOrderId);
}
});
it('should create and manage order through complete lifecycle', () => {
// Create order via API
cy.createTestOrder().then((order) => {
testOrderId = order.id;
// Visit order details page
cy.visit(`/orders/${order.id}`);
// Verify order details
cy.get('[data-cy="order-id"]').should('contain', order.id);
cy.get('[data-cy="order-status"]').should('contain', 'pending');
cy.get('[data-cy="customer-email"]').should('contain', order.customer_email);
// Confirm order
cy.get('[data-cy="confirm-order"]').click();
cy.get('[data-cy="order-status"]').should('contain', 'confirmed');
// Add shipping information
cy.get('[data-cy="add-shipping"]').click();
cy.get('[data-cy="tracking-number"]').type('CY-TEST-123');
cy.get('[data-cy="carrier"]').select('UPS');
cy.get('[data-cy="submit-shipping"]').click();
// Verify shipping status
cy.get('[data-cy="order-status"]').should('contain', 'shipped');
cy.get('[data-cy="tracking-number"]').should('contain', 'CY-TEST-123');
// Verify API state
cy.verifyOrderStatus(order.id, 'shipped');
});
});
it('should handle order search and filtering', () => {
// Create multiple test orders
cy.createMultipleTestOrders(5).then((orders) => {
orders.forEach(order => testOrderId = order.id); // Keep last for cleanup
cy.visit('/orders');
// Test search functionality
cy.get('[data-cy="order-search"]').type(orders[0].customer_email);
cy.get('[data-cy="search-results"]').should('contain', orders[0].id);
// Test status filtering
cy.get('[data-cy="status-filter"]').select('pending');
cy.get('[data-cy="order-list"] tr').should('have.length.at.least', 1);
// Test date range filtering
cy.get('[data-cy="date-from"]').type('2023-01-01');
cy.get('[data-cy="date-to"]').type('2023-12-31');
cy.get('[data-cy="apply-filters"]').click();
cy.get('[data-cy="order-list"]').should('be.visible');
});
});
});
// cypress/support/commands.js
Cypress.Commands.add('login', () => {
cy.session('login', () => {
cy.visit('/login');
cy.get('[data-cy="email"]').type(Cypress.env('TEST_USER_EMAIL'));
cy.get('[data-cy="password"]').type(Cypress.env('TEST_USER_PASSWORD'));
cy.get('[data-cy="login-button"]').click();
cy.url().should('include', '/dashboard');
});
});
Cypress.Commands.add('createTestOrder', () => {
return cy.request({
method: 'POST',
url: `${Cypress.env('API_BASE_URL')}/orders`,
headers: {
'Authorization': `Bearer ${Cypress.env('STATESET_API_KEY')}`,
'Content-Type': 'application/json'
},
body: {
customer_email: 'cypress-test@example.com',
items: [
{
sku: 'CY-TEST-ITEM',
quantity: 1,
price: 2999
}
],
shipping_address: {
line1: '123 Cypress Street',
city: 'Test City',
state: 'CA',
postal_code: '90210',
country: 'US'
}
}
}).then((response) => response.body);
});
Cypress.Commands.add('verifyOrderStatus', (orderId, expectedStatus) => {
cy.request({
method: 'GET',
url: `${Cypress.env('API_BASE_URL')}/orders/${orderId}`,
headers: {
'Authorization': `Bearer ${Cypress.env('STATESET_API_KEY')}`
}
}).then((response) => {
expect(response.body.status).to.equal(expectedStatus);
});
});
Cypress.Commands.add('cleanupOrder', (orderId) => {
cy.request({
method: 'DELETE',
url: `${Cypress.env('API_BASE_URL')}/orders/${orderId}`,
headers: {
'Authorization': `Bearer ${Cypress.env('STATESET_API_KEY')}`
},
failOnStatusCode: false
});
});
Performance Testing
Load Testing with Artillery
Copy
Ask AI
# artillery-config.yml
config:
target: 'https://api.sandbox.stateset.com'
phases:
- duration: 60
arrivalRate: 5
name: "Warm up"
- duration: 120
arrivalRate: 10
name: "Load test"
- duration: 60
arrivalRate: 20
name: "Stress test"
variables:
api_key: "{{ $env.STATESET_TEST_API_KEY }}"
processor: "./test-helpers.js"
scenarios:
- name: "Order Management"
weight: 70
flow:
- post:
url: "/v1/orders"
headers:
Authorization: "Bearer {{ api_key }}"
Content-Type: "application/json"
json:
customer_email: "load-test-{{ $randomString() }}@example.com"
items:
- sku: "LOAD-TEST-ITEM"
quantity: "{{ $randomInt(1, 5) }}"
price: 1999
shipping_address:
line1: "123 Load Test St"
city: "Test City"
state: "CA"
postal_code: "90210"
country: "US"
capture:
- json: "$.id"
as: "orderId"
- get:
url: "/v1/orders/{{ orderId }}"
headers:
Authorization: "Bearer {{ api_key }}"
- put:
url: "/v1/orders/{{ orderId }}"
headers:
Authorization: "Bearer {{ api_key }}"
Content-Type: "application/json"
json:
status: "confirmed"
- name: "Order Listing"
weight: 30
flow:
- get:
url: "/v1/orders"
qs:
limit: "{{ $randomInt(10, 50) }}"
status: "pending"
headers:
Authorization: "Bearer {{ api_key }}"
Stress Testing
Copy
Ask AI
// stress-test.js
import { StateSetClient } from 'stateset-node';
import { performance } from 'perf_hooks';
class StressTest {
constructor() {
this.client = new StateSetClient({
apiKey: process.env.STATESET_TEST_API_KEY,
environment: 'sandbox'
});
this.metrics = {
requests: 0,
errors: 0,
latencies: []
};
}
async runConcurrentOrders(concurrency = 10, duration = 60000) {
console.log(`Starting stress test: ${concurrency} concurrent users for ${duration}ms`);
const startTime = Date.now();
const workers = [];
// Start concurrent workers
for (let i = 0; i < concurrency; i++) {
workers.push(this.orderWorker(startTime + duration));
}
// Wait for all workers to complete
await Promise.all(workers);
// Calculate results
const totalDuration = Date.now() - startTime;
const avgLatency = this.metrics.latencies.reduce((a, b) => a + b, 0) / this.metrics.latencies.length;
const errorRate = (this.metrics.errors / this.metrics.requests) * 100;
const throughput = this.metrics.requests / (totalDuration / 1000);
console.log('Stress Test Results:');
console.log(`Total Requests: ${this.metrics.requests}`);
console.log(`Errors: ${this.metrics.errors} (${errorRate.toFixed(2)}%)`);
console.log(`Average Latency: ${avgLatency.toFixed(2)}ms`);
console.log(`Throughput: ${throughput.toFixed(2)} req/sec`);
return {
requests: this.metrics.requests,
errors: this.metrics.errors,
errorRate,
avgLatency,
throughput
};
}
async orderWorker(endTime) {
while (Date.now() < endTime) {
const start = performance.now();
try {
await this.createTestOrder();
this.metrics.requests++;
this.metrics.latencies.push(performance.now() - start);
} catch (error) {
this.metrics.errors++;
console.error('Order creation failed:', error.message);
}
// Small delay to prevent overwhelming
await new Promise(resolve => setTimeout(resolve, 100));
}
}
async createTestOrder() {
const orderData = {
customer_email: `stress-test-${Date.now()}@example.com`,
items: [
{
sku: 'STRESS-TEST-ITEM',
quantity: Math.floor(Math.random() * 5) + 1,
price: Math.floor(Math.random() * 5000) + 1000
}
],
shipping_address: {
line1: '123 Stress Test Street',
city: 'Test City',
state: 'CA',
postal_code: '90210',
country: 'US'
}
};
return this.client.orders.create(orderData);
}
}
// Run stress test
const stressTest = new StressTest();
stressTest.runConcurrentOrders(20, 120000) // 20 concurrent users for 2 minutes
.then(results => {
console.log('Stress test completed successfully');
process.exit(0);
})
.catch(error => {
console.error('Stress test failed:', error);
process.exit(1);
});
Testing Best Practices
Test Organization
- Separate unit, integration, and E2E tests
- Use descriptive test names
- Group related tests together
- Follow AAA pattern (Arrange, Act, Assert)
Test Data Management
- Use factories for test data generation
- Clean up test data after tests
- Use isolated test environments
- Mock external dependencies
CI/CD Integration
- Run tests on every commit
- Separate fast and slow test suites
- Use parallel test execution
- Generate coverage reports
Monitoring & Alerting
- Monitor test execution times
- Alert on test failures
- Track test coverage trends
- Performance regression detection
Continuous Integration Setup
Copy
Ask AI
# .github/workflows/test.yml
name: Test Suite
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run test:unit
env:
STATESET_TEST_API_KEY: ${{ secrets.STATESET_TEST_API_KEY }}
- name: Upload coverage reports
uses: codecov/codecov-action@v3
integration-tests:
runs-on: ubuntu-latest
needs: unit-tests
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run test:integration
env:
STATESET_TEST_API_KEY: ${{ secrets.STATESET_TEST_API_KEY }}
e2e-tests:
runs-on: ubuntu-latest
needs: integration-tests
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npx playwright install
- run: npm run test:e2e
env:
STATESET_TEST_API_KEY: ${{ secrets.STATESET_TEST_API_KEY }}
DASHBOARD_URL: ${{ secrets.DASHBOARD_URL }}
Next Steps
After implementing comprehensive testing:
- Monitor Test Metrics: Track coverage, execution time, and flakiness
- Expand Test Scenarios: Add edge cases and error conditions
- Performance Baselines: Establish performance benchmarks
- Test Automation: Integrate with deployment pipelines
For more testing strategies, see:
On this page
- Comprehensive Testing Guide
- Testing Overview
- Test Environment Setup
- Testing Pyramid Strategy
- Environment Configuration
- Unit Testing
- Testing Individual Functions
- Integration Testing
- Testing API Interactions
- End-to-End Testing
- Complete Workflow Testing
- Performance Testing
- Load Testing with Artillery
- Stress Testing
- Testing Best Practices
- Continuous Integration Setup
- Next Steps
Assistant
Responses are generated using AI and may contain mistakes.