Explorer reports addition
We have added a new Explorer feature to reports, with a timeline scrubber and easy anomaly detection.
Basic inventory API testing for stock management, updates, and availability checks
LoadForge can record your browser, graphically build tests, scan your site with a wizard and more. Sign up now to run your first test.
This guide shows how to test inventory management APIs with common operations like checking stock, updating inventory, and managing product availability.
from locust import task, HttpUser
import random
import json
class InventoryTestUser(HttpUser):
def on_start(self):
# Sample product data for testing
self.products = [
{"id": "PROD001", "sku": "LAPTOP-HP-001", "name": "HP Laptop"},
{"id": "PROD002", "sku": "PHONE-IPH-001", "name": "iPhone 13"},
{"id": "PROD003", "sku": "HEADP-SON-001", "name": "Sony Headphones"},
{"id": "PROD004", "sku": "TABLET-IPD-001", "name": "iPad Pro"},
{"id": "PROD005", "sku": "WATCH-APL-001", "name": "Apple Watch"}
]
# Inventory operations
self.operations = ["restock", "adjustment", "sale", "return"]
# API endpoints
self.inventory_endpoints = {
"check": "/api/inventory/check",
"update": "/api/inventory/update",
"bulk": "/api/inventory/bulk-update",
"alerts": "/api/inventory/alerts"
}
@task(4)
def test_check_inventory(self):
"""Test checking inventory levels"""
product = random.choice(self.products)
params = {
"sku": product["sku"],
"location": random.choice(["warehouse_1", "store_001", "online"])
}
with self.client.get(
self.inventory_endpoints["check"],
params=params,
name="Check Inventory"
) as response:
if response.status_code == 200:
try:
inventory = response.json()
available = inventory.get("available", 0)
reserved = inventory.get("reserved", 0)
total = inventory.get("total", 0)
print(f"Inventory check {product['sku']}: {available}/{total} available")
# Validate inventory data
if available < 0:
response.failure("Negative available inventory")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
elif response.status_code == 404:
print(f"Product not found: {product['sku']}")
else:
response.failure(f"Inventory check failed: {response.status_code}")
@task(3)
def test_update_inventory(self):
"""Test updating inventory levels"""
product = random.choice(self.products)
operation = random.choice(self.operations)
# Generate appropriate quantity based on operation
if operation == "restock":
quantity = random.randint(10, 100)
elif operation == "sale":
quantity = -random.randint(1, 5)
elif operation == "return":
quantity = random.randint(1, 3)
else: # adjustment
quantity = random.randint(-10, 10)
update_data = {
"sku": product["sku"],
"quantity": quantity,
"operation": operation,
"location": "warehouse_1",
"reason": f"Test {operation} operation"
}
with self.client.post(
self.inventory_endpoints["update"],
json=update_data,
name="Update Inventory"
) as response:
if response.status_code == 200:
try:
result = response.json()
new_quantity = result.get("new_quantity", 0)
print(f"Inventory updated {product['sku']}: {operation} {quantity}, new total: {new_quantity}")
# Validate update
if new_quantity < 0 and operation != "adjustment":
print(f"Warning: Negative inventory after {operation}")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
elif response.status_code == 400:
print(f"Invalid inventory update: {operation} {quantity}")
elif response.status_code == 409:
print(f"Inventory conflict: insufficient stock for {operation}")
else:
response.failure(f"Inventory update failed: {response.status_code}")
@task(2)
def test_bulk_inventory_update(self):
"""Test bulk inventory updates"""
# Select 2-3 products for bulk update
selected_products = random.sample(self.products, random.randint(2, 3))
bulk_updates = []
for product in selected_products:
bulk_updates.append({
"sku": product["sku"],
"quantity": random.randint(5, 50),
"operation": "restock",
"location": "warehouse_1"
})
bulk_data = {
"updates": bulk_updates,
"batch_id": f"BATCH_{random.randint(1000, 9999)}"
}
with self.client.post(
self.inventory_endpoints["bulk"],
json=bulk_data,
name="Bulk Inventory Update"
) as response:
if response.status_code == 200:
try:
result = response.json()
processed = result.get("processed", 0)
failed = result.get("failed", 0)
batch_id = result.get("batch_id")
print(f"Bulk update: {processed} processed, {failed} failed (batch: {batch_id})")
if failed > 0:
print(f"Some bulk updates failed: {failed}/{len(bulk_updates)}")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Bulk inventory update failed: {response.status_code}")
@task(2)
def test_inventory_availability(self):
"""Test inventory availability checking"""
product = random.choice(self.products)
requested_quantity = random.randint(1, 10)
availability_params = {
"sku": product["sku"],
"quantity": requested_quantity,
"location": random.choice(["warehouse_1", "store_001", "all"])
}
with self.client.get(
f"{self.inventory_endpoints['check']}/availability",
params=availability_params,
name="Check Availability"
) as response:
if response.status_code == 200:
try:
availability = response.json()
available = availability.get("available", False)
can_fulfill = availability.get("can_fulfill", 0)
locations = availability.get("locations", [])
print(f"Availability {product['sku']}: {can_fulfill}/{requested_quantity} available")
if not available and can_fulfill > 0:
print(f"Inconsistent availability data")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Availability check failed: {response.status_code}")
@task(1)
def test_low_stock_alerts(self):
"""Test low stock alert system"""
with self.client.get(
self.inventory_endpoints["alerts"],
params={"threshold": random.choice([5, 10, 20])},
name="Low Stock Alerts"
) as response:
if response.status_code == 200:
try:
alerts = response.json()
if isinstance(alerts, dict):
items = alerts.get("alerts", alerts.get("items", []))
else:
items = alerts if isinstance(alerts, list) else []
print(f"Low stock alerts: {len(items)} products below threshold")
# Validate alert data
for alert in items[:3]: # Check first 3 alerts
if isinstance(alert, dict):
sku = alert.get("sku")
current_stock = alert.get("current_stock", 0)
threshold = alert.get("threshold", 0)
if current_stock > threshold:
print(f"Invalid alert: {sku} stock {current_stock} > threshold {threshold}")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Low stock alerts failed: {response.status_code}")
@task(1)
def test_inventory_reservation(self):
"""Test inventory reservation for orders"""
product = random.choice(self.products)
reserve_quantity = random.randint(1, 5)
reservation_data = {
"sku": product["sku"],
"quantity": reserve_quantity,
"order_id": f"ORDER_{random.randint(10000, 99999)}",
"expires_in": 900 # 15 minutes
}
with self.client.post(
f"{self.inventory_endpoints['update']}/reserve",
json=reservation_data,
name="Reserve Inventory"
) as response:
if response.status_code == 200:
try:
result = response.json()
reservation_id = result.get("reservation_id")
reserved_quantity = result.get("reserved_quantity", 0)
expires_at = result.get("expires_at")
print(f"Reserved {reserved_quantity} units of {product['sku']} (ID: {reservation_id})")
# Test releasing the reservation
if reservation_id:
self._release_reservation(reservation_id)
except json.JSONDecodeError:
response.failure("Invalid JSON response")
elif response.status_code == 409:
print(f"Insufficient inventory to reserve {reserve_quantity} units")
else:
response.failure(f"Inventory reservation failed: {response.status_code}")
def _release_reservation(self, reservation_id):
"""Helper method to release inventory reservation"""
with self.client.delete(
f"{self.inventory_endpoints['update']}/reserve/{reservation_id}",
name="Release Reservation"
) as response:
if response.status_code == 200:
print(f"Released reservation: {reservation_id}")
else:
print(f"Failed to release reservation: {reservation_id}")
This guide provides comprehensive inventory management testing patterns for e-commerce and retail applications with proper stock tracking and warehouse operations.