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
API Testing Guide
Comprehensive guide to testing Stateset APIs with examples for unit, integration, and end-to-end testing
API Testing Guide
Learn how to thoroughly test your Stateset API integration with comprehensive testing strategies, tools, and real-world examples. This guide covers everything from unit tests to end-to-end testing scenarios.
Prerequisites
Before you begin, ensure you have:
- A Stateset test environment account
- Testing API credentials (separate from production)
- Node.js 16+ installed
- Basic understanding of testing frameworks
- Access to testing tools (Jest, Mocha, etc.)
Testing Environment Setup
Configure Test Environment
Set up separate test credentials and environment:
# .env.test
STATESET_API_KEY=sk_test_51H9x2C2QlDjKpM2WYw5...
STATESET_ENVIRONMENT=test
STATESET_BASE_URL=https://api-test.stateset.com
# Test Database
TEST_DATABASE_URL=postgresql://test_user:test_password@localhost:5432/stateset_test
# Mock Settings
ENABLE_API_MOCKING=true
MOCK_EXTERNAL_SERVICES=true
Install Testing Dependencies
Install necessary testing tools and libraries:
npm install --save-dev \
jest \
supertest \
nock \
@testing-library/jest-dom \
msw \
faker \
test-containers
# For TypeScript projects
npm install --save-dev \
@types/jest \
@types/supertest \
ts-jest
Configure Jest
Create comprehensive Jest configuration:
// jest.config.js
module.exports = {
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
testMatch: [
'**/tests/**/*.test.js',
'**/tests/**/*.spec.js'
],
collectCoverageFrom: [
'src/**/*.js',
'!src/**/*.test.js',
'!src/tests/**'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
testTimeout: 30000,
maxWorkers: 4
};
Unit Testing
Testing API Client Methods
Test your Stateset API client methods in isolation:
// tests/unit/stateset-client.test.js
import { StateSetClient } from 'stateset-node';
import nock from 'nock';
describe('StateSetClient', () => {
let client;
const apiKey = process.env.STATESET_API_KEY;
const baseURL = 'https://api-test.stateset.com';
beforeEach(() => {
client = new StateSetClient({
apiKey,
baseURL
});
// Clean up any pending HTTP mocks
nock.cleanAll();
});
afterEach(() => {
nock.cleanAll();
});
describe('Orders API', () => {
it('should create an order successfully', async () => {
const orderData = {
customer_email: 'test.customer@yourstore.com',
items: [
{
sku: 'TEST-001',
quantity: 2,
price: 29.99
}
],
shipping_address: {
street1: '123 Test St',
city: 'Test City',
state: 'TS',
zip: '12345',
country: 'US'
}
};
const expectedResponse = {
id: 'ord_test_123',
status: 'pending',
...orderData
};
// Mock the API call
nock(baseURL)
.post('/v1/orders')
.reply(201, expectedResponse);
const result = await client.orders.create(orderData);
expect(result).toEqual(expectedResponse);
expect(result.id).toBe('ord_test_123');
expect(result.status).toBe('pending');
});
it('should handle order creation errors', async () => {
const invalidOrderData = {
// Missing required fields
items: []
};
nock(baseURL)
.post('/v1/orders')
.reply(400, {
error: {
code: 'VALIDATION_ERROR',
message: 'Missing required field: customer_email'
}
});
await expect(client.orders.create(invalidOrderData))
.rejects
.toThrow('Missing required field: customer_email');
});
it('should list orders with pagination', async () => {
const expectedOrders = {
orders: [
{ id: 'ord_1', status: 'processing' },
{ id: 'ord_2', status: 'shipped' }
],
total_count: 2,
has_more: false
};
nock(baseURL)
.get('/v1/orders')
.query({ limit: 10, offset: 0 })
.reply(200, expectedOrders);
const result = await client.orders.list({ limit: 10, offset: 0 });
expect(result.orders).toHaveLength(2);
expect(result.total_count).toBe(2);
expect(result.has_more).toBe(false);
});
});
describe('Inventory API', () => {
it('should update inventory levels', async () => {
const inventoryUpdate = {
sku: 'TEST-001',
quantity: 100,
location: 'warehouse_1'
};
const expectedResponse = {
sku: 'TEST-001',
available_quantity: 100,
location: 'warehouse_1',
updated_at: '2024-01-15T10:30:00Z'
};
nock(baseURL)
.put('/v1/inventory/TEST-001')
.reply(200, expectedResponse);
const result = await client.inventory.update('TEST-001', inventoryUpdate);
expect(result.available_quantity).toBe(100);
expect(result.sku).toBe('TEST-001');
});
it('should handle insufficient inventory errors', async () => {
nock(baseURL)
.put('/v1/inventory/TEST-001')
.reply(400, {
error: {
code: 'INSUFFICIENT_INVENTORY',
message: 'Cannot reduce inventory below safety stock level'
}
});
await expect(client.inventory.update('TEST-001', { quantity: -1000 }))
.rejects
.toThrow('Cannot reduce inventory below safety stock level');
});
});
describe('Error Handling', () => {
it('should retry on transient errors', async () => {
nock(baseURL)
.get('/v1/orders/ord_123')
.reply(500, { error: 'Internal Server Error' })
.get('/v1/orders/ord_123')
.reply(500, { error: 'Internal Server Error' })
.get('/v1/orders/ord_123')
.reply(200, { id: 'ord_123', status: 'processing' });
const result = await client.orders.get('ord_123');
expect(result.id).toBe('ord_123');
expect(result.status).toBe('processing');
});
it('should respect rate limits', async () => {
nock(baseURL)
.get('/v1/orders')
.reply(429, {
error: 'Rate limit exceeded',
retry_after: 1
})
.get('/v1/orders')
.reply(200, { orders: [], total_count: 0 });
const startTime = Date.now();
const result = await client.orders.list();
const endTime = Date.now();
// Should have waited at least 1 second
expect(endTime - startTime).toBeGreaterThan(1000);
expect(result.orders).toEqual([]);
});
});
});
Testing Business Logic
Test your business logic that uses the Stateset API:
// tests/unit/order-manager.test.js
import { OrderManager } from '../../src/services/order-manager';
import { StateSetClient } from 'stateset-node';
// Mock the entire Stateset client
jest.mock('stateset-node');
describe('OrderManager', () => {
let orderManager;
let mockClient;
beforeEach(() => {
mockClient = {
orders: {
create: jest.fn(),
update: jest.fn(),
get: jest.fn()
},
inventory: {
allocate: jest.fn(),
get: jest.fn()
},
shipping: {
createLabel: jest.fn()
}
};
StateSetClient.mockImplementation(() => mockClient);
orderManager = new OrderManager('test-api-key');
});
describe('processOrder', () => {
it('should process a complete order successfully', async () => {
const orderData = {
customer_email: 'test.customer@yourstore.com',
items: [{ sku: 'TEST-001', quantity: 2 }]
};
// Mock successful API responses
mockClient.orders.create.mockResolvedValue({
id: 'ord_123',
status: 'created'
});
mockClient.inventory.allocate.mockResolvedValue({
allocated: true,
items: [{ sku: 'TEST-001', quantity: 2, location: 'warehouse_1' }]
});
mockClient.orders.update.mockResolvedValue({
id: 'ord_123',
status: 'allocated'
});
mockClient.shipping.createLabel.mockResolvedValue({
tracking_number: 'TRACK123',
label_url: 'https://example.com/label.pdf'
});
const result = await orderManager.processOrder(orderData);
expect(result.success).toBe(true);
expect(result.order.id).toBe('ord_123');
expect(result.tracking_number).toBe('TRACK123');
// Verify the correct sequence of API calls
expect(mockClient.orders.create).toHaveBeenCalledWith(orderData);
expect(mockClient.inventory.allocate).toHaveBeenCalled();
expect(mockClient.shipping.createLabel).toHaveBeenCalled();
});
it('should handle inventory allocation failures', async () => {
const orderData = {
customer_email: 'test.customer@yourstore.com',
items: [{ sku: 'OUT-OF-STOCK', quantity: 10 }]
};
mockClient.orders.create.mockResolvedValue({
id: 'ord_123',
status: 'created'
});
mockClient.inventory.allocate.mockRejectedValue(
new Error('Insufficient inventory for SKU: OUT-OF-STOCK')
);
mockClient.orders.update.mockResolvedValue({
id: 'ord_123',
status: 'allocation_failed'
});
const result = await orderManager.processOrder(orderData);
expect(result.success).toBe(false);
expect(result.error).toContain('Insufficient inventory');
expect(mockClient.orders.update).toHaveBeenCalledWith('ord_123', {
status: 'allocation_failed'
});
});
});
describe('calculateShippingCost', () => {
it('should calculate shipping cost based on weight and distance', () => {
const order = {
items: [
{ weight: 1.5, quantity: 2 },
{ weight: 0.5, quantity: 1 }
],
shipping_address: { country: 'US', state: 'CA' }
};
const cost = orderManager.calculateShippingCost(order);
expect(cost).toBeGreaterThan(0);
expect(typeof cost).toBe('number');
});
it('should apply free shipping for orders over threshold', () => {
const order = {
total_amount: 100,
items: [{ weight: 1, quantity: 1 }],
shipping_address: { country: 'US' }
};
const cost = orderManager.calculateShippingCost(order);
expect(cost).toBe(0); // Free shipping over $75
});
});
});
Integration Testing
Testing API Endpoints
Test your API endpoints with real HTTP requests:
// tests/integration/orders-api.test.js
import request from 'supertest';
import app from '../../src/app';
import { setupTestDatabase, teardownTestDatabase } from '../helpers/database';
describe('Orders API Integration', () => {
beforeAll(async () => {
await setupTestDatabase();
});
afterAll(async () => {
await teardownTestDatabase();
});
describe('POST /api/orders', () => {
it('should create a new order', async () => {
const orderData = {
customer_email: 'integration@test.com',
items: [
{
sku: 'INTEGRATION-001',
quantity: 1,
price: 49.99
}
],
shipping_address: {
street1: '123 Integration St',
city: 'Test City',
state: 'TS',
zip: '12345',
country: 'US'
}
};
const response = await request(app)
.post('/api/orders')
.send(orderData)
.expect(201);
expect(response.body).toHaveProperty('id');
expect(response.body.status).toBe('pending');
expect(response.body.customer_email).toBe('integration@test.com');
// Verify order was created in Stateset
const createdOrder = await request(app)
.get(`/api/orders/${response.body.id}`)
.expect(200);
expect(createdOrder.body.id).toBe(response.body.id);
});
it('should validate required fields', async () => {
const invalidOrder = {
items: [] // Missing customer_email and valid items
};
const response = await request(app)
.post('/api/orders')
.send(invalidOrder)
.expect(400);
expect(response.body.error).toContain('customer_email is required');
});
});
describe('GET /api/orders', () => {
beforeEach(async () => {
// Create test orders
await request(app)
.post('/api/orders')
.send({
customer_email: 'test1@example.com',
items: [{ sku: 'TEST-001', quantity: 1, price: 10 }]
});
await request(app)
.post('/api/orders')
.send({
customer_email: 'test2@example.com',
items: [{ sku: 'TEST-002', quantity: 2, price: 20 }]
});
});
it('should list orders with pagination', async () => {
const response = await request(app)
.get('/api/orders?limit=1&offset=0')
.expect(200);
expect(response.body.orders).toHaveLength(1);
expect(response.body).toHaveProperty('total_count');
expect(response.body).toHaveProperty('has_more');
});
it('should filter orders by status', async () => {
const response = await request(app)
.get('/api/orders?status=pending')
.expect(200);
expect(response.body.orders.every(order => order.status === 'pending')).toBe(true);
});
it('should search orders by customer email', async () => {
const response = await request(app)
.get('/api/orders?customer_email=test1@example.com')
.expect(200);
expect(response.body.orders).toHaveLength(1);
expect(response.body.orders[0].customer_email).toBe('test1@example.com');
});
});
});
Testing Webhook Handlers
Test webhook event processing:
// tests/integration/webhooks.test.js
import request from 'supertest';
import crypto from 'crypto';
import app from '../../src/app';
describe('Webhook Integration', () => {
const webhookSecret = 'test_webhook_secret';
function signWebhookPayload(payload) {
const signature = crypto
.createHmac('sha256', webhookSecret)
.update(JSON.stringify(payload))
.digest('hex');
return `sha256=${signature}`;
}
describe('POST /webhooks/stateset', () => {
it('should process order.created webhook', async () => {
const webhookPayload = {
event: 'order.created',
data: {
id: 'ord_webhook_test',
status: 'pending',
customer_email: 'webhook@test.com',
total_amount: 99.99
}
};
const signature = signWebhookPayload(webhookPayload);
const response = await request(app)
.post('/webhooks/stateset')
.set('X-Stateset-Signature', signature)
.send(webhookPayload)
.expect(200);
expect(response.body.received).toBe(true);
// Verify the webhook was processed
// Check database, logs, or external service calls
});
it('should reject webhooks with invalid signatures', async () => {
const webhookPayload = {
event: 'order.created',
data: { id: 'ord_test' }
};
await request(app)
.post('/webhooks/stateset')
.set('X-Stateset-Signature', 'invalid-signature')
.send(webhookPayload)
.expect(401);
});
it('should handle unknown webhook events gracefully', async () => {
const webhookPayload = {
event: 'unknown.event',
data: { id: 'test' }
};
const signature = signWebhookPayload(webhookPayload);
const response = await request(app)
.post('/webhooks/stateset')
.set('X-Stateset-Signature', signature)
.send(webhookPayload)
.expect(200);
expect(response.body.received).toBe(true);
});
});
});
End-to-End Testing
Complete Order Workflow Testing
Test the entire order lifecycle from creation to fulfillment:
// tests/e2e/order-lifecycle.test.js
import { StateSetClient } from 'stateset-node';
import { faker } from '@faker-js/faker';
describe('Order Lifecycle E2E', () => {
let client;
let testOrder;
beforeAll(() => {
client = new StateSetClient({
apiKey: process.env.STATESET_TEST_API_KEY,
environment: 'test'
});
});
describe('Complete Order Flow', () => {
it('should process order from creation to delivery', async () => {
// Step 1: Create order
const orderData = {
customer_email: faker.internet.email(),
customer_name: faker.person.fullName(),
items: [
{
sku: 'E2E-TEST-001',
quantity: 2,
price: 25.99,
name: 'Test Product'
}
],
shipping_address: {
name: faker.person.fullName(),
street1: faker.location.streetAddress(),
city: faker.location.city(),
state: faker.location.state({ abbreviated: true }),
zip: faker.location.zipCode(),
country: 'US'
},
payment_method: {
type: 'test_card',
token: 'tok_test_visa'
}
};
testOrder = await client.orders.create(orderData);
expect(testOrder.id).toBeDefined();
expect(testOrder.status).toBe('pending');
// Step 2: Wait for payment processing
await waitForOrderStatus(testOrder.id, 'payment_confirmed', 30000);
// Step 3: Allocate inventory
const allocation = await client.inventory.allocate({
order_id: testOrder.id,
items: orderData.items
});
expect(allocation.success).toBe(true);
expect(allocation.items).toHaveLength(1);
// Step 4: Create fulfillment
const fulfillment = await client.fulfillment.create({
order_id: testOrder.id,
location_id: 'test_warehouse',
items: orderData.items
});
expect(fulfillment.id).toBeDefined();
expect(fulfillment.status).toBe('pending');
// Step 5: Generate shipping label
const shippingLabel = await client.shipping.createLabel({
fulfillment_id: fulfillment.id,
carrier: 'test_carrier',
service: 'ground'
});
expect(shippingLabel.tracking_number).toBeDefined();
expect(shippingLabel.label_url).toBeDefined();
// Step 6: Mark as shipped
const shippedOrder = await client.orders.update(testOrder.id, {
status: 'shipped',
tracking_number: shippingLabel.tracking_number
});
expect(shippedOrder.status).toBe('shipped');
expect(shippedOrder.tracking_number).toBe(shippingLabel.tracking_number);
// Step 7: Simulate delivery
const deliveredOrder = await client.orders.update(testOrder.id, {
status: 'delivered',
delivered_at: new Date().toISOString()
});
expect(deliveredOrder.status).toBe('delivered');
expect(deliveredOrder.delivered_at).toBeDefined();
}, 60000); // 60 second timeout for complete flow
async function waitForOrderStatus(orderId, expectedStatus, timeout = 10000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const order = await client.orders.get(orderId);
if (order.status === expectedStatus) {
return order;
}
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second
}
throw new Error(`Order ${orderId} did not reach status ${expectedStatus} within ${timeout}ms`);
}
});
describe('Return Processing', () => {
it('should process return for delivered order', async () => {
// Ensure we have a delivered order
expect(testOrder.status).toBe('delivered');
// Step 1: Create return
const returnData = {
order_id: testOrder.id,
items: [
{
sku: 'E2E-TEST-001',
quantity: 1,
reason: 'defective',
condition: 'damaged'
}
],
reason_code: 'defective',
customer_notes: 'Product arrived damaged'
};
const returnRecord = await client.returns.create(returnData);
expect(returnRecord.id).toBeDefined();
expect(returnRecord.status).toBe('pending');
// Step 2: Generate return label
const returnLabel = await client.shipping.createReturnLabel({
return_id: returnRecord.id,
carrier: 'test_carrier'
});
expect(returnLabel.tracking_number).toBeDefined();
// Step 3: Process return receipt
const receivedReturn = await client.returns.update(returnRecord.id, {
status: 'received',
received_at: new Date().toISOString()
});
expect(receivedReturn.status).toBe('received');
// Step 4: Process refund
const refund = await client.refunds.create({
return_id: returnRecord.id,
amount: 25.99,
reason: 'defective_product'
});
expect(refund.id).toBeDefined();
expect(refund.amount).toBe(25.99);
expect(refund.status).toBe('processed');
});
});
});
Performance Testing
Load Testing
Test API performance under load:
// tests/performance/load-test.js
import { StateSetClient } from 'stateset-node';
import { performance } from 'perf_hooks';
describe('Load Testing', () => {
let client;
beforeAll(() => {
client = new StateSetClient({
apiKey: process.env.STATESET_TEST_API_KEY,
environment: 'test'
});
});
describe('Order Creation Performance', () => {
it('should handle 100 concurrent order creations', async () => {
const concurrentRequests = 100;
const orderPromises = [];
const startTime = performance.now();
for (let i = 0; i < concurrentRequests; i++) {
const orderData = {
customer_email: `load-test-${i}@example.com`,
items: [
{
sku: 'LOAD-TEST-001',
quantity: 1,
price: 10.00
}
]
};
orderPromises.push(client.orders.create(orderData));
}
const results = await Promise.allSettled(orderPromises);
const endTime = performance.now();
const successfulRequests = results.filter(r => r.status === 'fulfilled').length;
const failedRequests = results.filter(r => r.status === 'rejected').length;
const totalTime = endTime - startTime;
const averageTime = totalTime / concurrentRequests;
console.log(`Performance Results:
- Total requests: ${concurrentRequests}
- Successful: ${successfulRequests}
- Failed: ${failedRequests}
- Total time: ${totalTime.toFixed(2)}ms
- Average time per request: ${averageTime.toFixed(2)}ms
- Requests per second: ${(concurrentRequests / (totalTime / 1000)).toFixed(2)}`);
// Assert performance requirements
expect(successfulRequests).toBeGreaterThan(concurrentRequests * 0.95); // 95% success rate
expect(averageTime).toBeLessThan(1000); // Average response time under 1 second
}, 30000); // 30 second timeout
});
describe('API Rate Limiting', () => {
it('should handle rate limiting gracefully', async () => {
const requests = [];
const startTime = performance.now();
// Make many requests quickly to trigger rate limiting
for (let i = 0; i < 200; i++) {
requests.push(
client.orders.list({ limit: 1 })
.catch(error => ({ error: error.message }))
);
}
const results = await Promise.all(requests);
const endTime = performance.now();
const successful = results.filter(r => !r.error).length;
const rateLimited = results.filter(r =>
r.error && r.error.includes('rate limit')
).length;
console.log(`Rate Limiting Results:
- Total requests: ${requests.length}
- Successful: ${successful}
- Rate limited: ${rateLimited}
- Time taken: ${(endTime - startTime).toFixed(2)}ms`);
expect(rateLimited).toBeGreaterThan(0); // Should encounter rate limiting
expect(successful).toBeGreaterThan(0); // Some requests should succeed
});
});
});
Test Data Management
Test Data Factory
Create realistic test data:
// tests/helpers/test-data-factory.js
import { faker } from '@faker-js/faker';
export class TestDataFactory {
static createOrderData(overrides = {}) {
return {
customer_email: faker.internet.email(),
customer_name: faker.person.fullName(),
items: [
{
sku: faker.string.alphanumeric(8).toUpperCase(),
quantity: faker.number.int({ min: 1, max: 5 }),
price: parseFloat(faker.commerce.price()),
name: faker.commerce.productName()
}
],
shipping_address: this.createAddress(),
billing_address: this.createAddress(),
...overrides
};
}
static createAddress() {
return {
name: faker.person.fullName(),
company: faker.company.name(),
street1: faker.location.streetAddress(),
street2: faker.location.secondaryAddress(),
city: faker.location.city(),
state: faker.location.state({ abbreviated: true }),
zip: faker.location.zipCode(),
country: faker.location.countryCode('alpha-2'),
phone: faker.phone.number()
};
}
static createInventoryItem(overrides = {}) {
return {
sku: faker.string.alphanumeric(8).toUpperCase(),
name: faker.commerce.productName(),
description: faker.commerce.productDescription(),
price: parseFloat(faker.commerce.price()),
cost: parseFloat(faker.commerce.price({ min: 5, max: 50 })),
weight: faker.number.float({ min: 0.1, max: 5.0 }),
category: faker.commerce.department(),
quantity: faker.number.int({ min: 0, max: 1000 }),
...overrides
};
}
static createCustomer(overrides = {}) {
return {
email: faker.internet.email(),
name: faker.person.fullName(),
phone: faker.phone.number(),
address: this.createAddress(),
tier: faker.helpers.arrayElement(['bronze', 'silver', 'gold', 'platinum']),
...overrides
};
}
}
Database Setup for Testing
// tests/helpers/database.js
import { Pool } from 'pg';
import { migrate } from 'postgres-migrations';
const testDbConfig = {
host: 'localhost',
port: 5432,
database: 'stateset_test',
user: 'test_user',
password: 'test_password'
};
let pool;
export async function setupTestDatabase() {
pool = new Pool(testDbConfig);
// Run migrations
await migrate(testDbConfig, 'migrations');
// Seed test data
await seedTestData();
}
export async function teardownTestDatabase() {
if (pool) {
await pool.end();
}
}
export async function cleanDatabase() {
const tables = [
'orders',
'order_line_items',
'inventory',
'customers',
'returns',
'fulfillments'
];
for (const table of tables) {
await pool.query(`TRUNCATE TABLE ${table} CASCADE`);
}
}
async function seedTestData() {
// Insert test inventory items
await pool.query(`
INSERT INTO inventory (sku, name, quantity, price) VALUES
('TEST-001', 'Test Product 1', 100, 25.99),
('TEST-002', 'Test Product 2', 50, 49.99),
('E2E-TEST-001', 'E2E Test Product', 1000, 25.99)
`);
// Insert test customers
await pool.query(`
INSERT INTO customers (email, name, tier) VALUES
('test@example.com', 'Test Customer', 'silver'),
('vip@example.com', 'VIP Customer', 'platinum')
`);
}
Continuous Integration
GitHub Actions Workflow
# .github/workflows/api-tests.yml
name: API Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:13
env:
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
POSTGRES_DB: stateset_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup test environment
run: |
cp .env.test.example .env.test
env:
STATESET_TEST_API_KEY: ${{ secrets.STATESET_TEST_API_KEY }}
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/stateset_test
- name: Run unit tests
run: npm run test:unit
- name: Run integration tests
run: npm run test:integration
- name: Run E2E tests
run: npm run test:e2e
- name: Run performance tests
run: npm run test:performance
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
Best Practices
Testing Strategy
- Test Pyramid: More unit tests, fewer integration tests, minimal E2E tests
- Isolation: Each test should be independent and not rely on others
- Deterministic: Tests should produce consistent results
- Fast Feedback: Unit tests should run quickly for rapid development
Test Organization
- Naming: Use descriptive test names that explain what is being tested
- Structure: Group related tests using describe blocks
- Setup/Teardown: Use beforeEach/afterEach for consistent test state
- Data Management: Use factories for generating test data
Mocking Guidelines
- Mock External Services: Don’t make real API calls in unit tests
- Test Behavior: Mock what you control, test what you own
- Realistic Mocks: Make mocks behave like the real service
- Verification: Verify that mocks are called with correct parameters
Performance Considerations
- Parallel Execution: Run tests in parallel when possible
- Resource Management: Clean up resources properly
- Test Data: Use minimal test data sets
- Timeouts: Set appropriate timeouts for async operations
Troubleshooting
- Check test environment configuration
- Verify API credentials are correct
- Ensure test database is properly set up
- Review mock configurations
- Add proper wait conditions for async operations
- Increase timeouts for slow operations
- Ensure proper test isolation
- Check for race conditions
- Use connection pooling for database tests
- Implement proper mocking for external services
- Run tests in parallel
- Profile slow tests
Next Steps
Webhook Testing
Learn advanced webhook testing strategies
Monitoring Guide
Set up monitoring for your API integration
Performance Optimization
Optimize your API integration performance
Security Testing
Implement security testing best practices
Conclusion
You now have a comprehensive testing strategy for your Stateset API integration that includes:
- ✅ Unit testing for individual components
- ✅ Integration testing for API endpoints
- ✅ End-to-end testing for complete workflows
- ✅ Performance testing for load scenarios
- ✅ Proper test data management
- ✅ CI/CD integration
- ✅ Best practices and troubleshooting
This testing approach ensures your integration is reliable, performant, and maintainable.
- API Testing Guide
- Prerequisites
- Testing Environment Setup
- Unit Testing
- Testing API Client Methods
- Testing Business Logic
- Integration Testing
- Testing API Endpoints
- Testing Webhook Handlers
- End-to-End Testing
- Complete Order Workflow Testing
- Performance Testing
- Load Testing
- Test Data Management
- Test Data Factory
- Database Setup for Testing
- Continuous Integration
- GitHub Actions Workflow
- Best Practices
- Testing Strategy
- Test Organization
- Mocking Guidelines
- Performance Considerations
- Troubleshooting
- Next Steps
- Conclusion