Testing Examples
January 18, 2026 · View on GitHub
Practical examples for writing tests and assertions in LazyCurl scripts.
Table of Contents
- Overview
- Basic Response Validation
- JSON Response Testing
- Error Response Testing
- Performance Testing
- Authentication Testing
- Data Validation Patterns
- Common Patterns
- See Also
Overview
LazyCurl provides Jest-like testing APIs:
| Function | Purpose |
|---|---|
lc.test(name, fn) | Define a named test case |
lc.expect(value) | Create assertions |
lc.expect(value).not.* | Negate assertions |
Test results appear in the Tests tab of the Response panel.
Basic Response Validation
Status Code Tests
// Test specific status code
lc.test("Returns 200 OK", function() {
lc.expect(lc.response.status).toBe(200);
});
// Test status code range
lc.test("Returns success status", function() {
lc.expect(lc.response.status).toBeGreaterThanOrEqual(200);
lc.expect(lc.response.status).toBeLessThan(300);
});
// Test for specific error
lc.test("Returns 404 for missing resource", function() {
lc.expect(lc.response.status).toBe(404);
});
Response Time Tests
// Basic performance check
lc.test("Response time under 2 seconds", function() {
lc.expect(lc.response.time).toBeLessThan(2000);
});
// Strict performance requirement
lc.test("Response time under 500ms", function() {
lc.expect(lc.response.time).toBeLessThan(500);
});
// Performance tiers
var time = lc.response.time;
if (time < 200) {
console.log("Performance: Excellent");
} else if (time < 500) {
console.log("Performance: Good");
} else {
console.warn("Performance: Needs improvement");
}
Content-Type Tests
lc.test("Returns JSON content", function() {
var contentType = lc.response.headers.get("Content-Type");
lc.expect(contentType).toContain("application/json");
});
lc.test("Returns correct content type", function() {
var contentType = lc.response.headers.get("Content-Type");
lc.expect(contentType).toBeDefined();
lc.expect(contentType).not.toContain("text/html");
});
JSON Response Testing
Basic Structure
var data = lc.response.body.json();
lc.test("Response is valid JSON", function() {
lc.expect(data).not.toBeNull();
});
lc.test("Response has required fields", function() {
lc.expect(data).toHaveProperty("id");
lc.expect(data).toHaveProperty("name");
lc.expect(data).toHaveProperty("email");
});
Nested Objects
var data = lc.response.body.json();
lc.test("User has address", function() {
lc.expect(data).toHaveProperty("address");
lc.expect(data.address).toHaveProperty("city");
lc.expect(data.address).toHaveProperty("country");
});
lc.test("User has contact info", function() {
lc.expect(data.contact).toBeDefined();
lc.expect(data.contact.email).toMatch("@");
});
Array Responses
var data = lc.response.body.json();
lc.test("Response is an array", function() {
lc.expect(Array.isArray(data.items)).toBe(true);
});
lc.test("Array is not empty", function() {
lc.expect(data.items.length).toBeGreaterThan(0);
});
lc.test("Array items have required fields", function() {
var firstItem = data.items[0];
lc.expect(firstItem).toHaveProperty("id");
lc.expect(firstItem).toHaveProperty("name");
});
lc.test("All items have IDs", function() {
var allHaveIds = data.items.every(function(item) {
return item.id !== undefined;
});
lc.expect(allHaveIds).toBe(true);
});
Pagination Response
var data = lc.response.body.json();
lc.test("Response includes pagination info", function() {
lc.expect(data).toHaveProperty("items");
lc.expect(data).toHaveProperty("total");
lc.expect(data).toHaveProperty("page");
lc.expect(data).toHaveProperty("pageSize");
});
lc.test("Pagination values are valid", function() {
lc.expect(data.page).toBeGreaterThanOrEqual(1);
lc.expect(data.pageSize).toBeGreaterThan(0);
lc.expect(data.total).toBeGreaterThanOrEqual(0);
});
lc.test("Items count matches page size", function() {
var itemCount = data.items.length;
lc.expect(itemCount).toBeLessThanOrEqual(data.pageSize);
});
console.log("Page " + data.page + ": " + data.items.length + "/" + data.total + " items");
Error Response Testing
Validation Error
// Test 400 Bad Request response
lc.test("Returns 400 for invalid input", function() {
lc.expect(lc.response.status).toBe(400);
});
var error = lc.response.body.json();
lc.test("Error response has message", function() {
lc.expect(error).toHaveProperty("message");
lc.expect(error.message).toBeDefined();
});
lc.test("Error includes validation details", function() {
lc.expect(error).toHaveProperty("errors");
lc.expect(Array.isArray(error.errors)).toBe(true);
});
// Log validation errors
if (error && error.errors) {
error.errors.forEach(function(err) {
console.log("Validation error: " + err.field + " - " + err.message);
});
}
Not Found Error
lc.test("Returns 404 Not Found", function() {
lc.expect(lc.response.status).toBe(404);
});
lc.test("Error message mentions resource", function() {
var error = lc.response.body.json();
lc.expect(error.message).toContain("not found");
});
Authentication Error
lc.test("Returns 401 Unauthorized", function() {
lc.expect(lc.response.status).toBe(401);
});
lc.test("Error indicates auth required", function() {
var error = lc.response.body.json();
lc.expect(error.code).toBe("UNAUTHORIZED");
});
// Clear stored tokens on 401
if (lc.response.status === 401) {
lc.env.unset("access_token");
console.warn("Authentication failed - token cleared");
}
Performance Testing
Response Time Thresholds
lc.test("Fast response (< 200ms)", function() {
lc.expect(lc.response.time).toBeLessThan(200);
});
lc.test("Acceptable response (< 1s)", function() {
lc.expect(lc.response.time).toBeLessThan(1000);
});
lc.test("Within SLA (< 2s)", function() {
lc.expect(lc.response.time).toBeLessThan(2000);
});
Performance Categories
var time = lc.response.time;
lc.test("Response time is acceptable", function() {
lc.expect(time).toBeLessThan(2000);
});
// Categorize performance
if (time < 100) {
console.log("Excellent: " + time + "ms");
} else if (time < 300) {
console.log("Good: " + time + "ms");
} else if (time < 1000) {
console.log("Acceptable: " + time + "ms");
} else {
console.warn("Slow: " + time + "ms - needs optimization");
}
Track Performance Over Time
// Store performance data
var perfHistory = lc.globals.get("perf_history") || [];
perfHistory.push({
endpoint: lc.request.url,
time: lc.response.time,
status: lc.response.status,
timestamp: Date.now()
});
// Keep last 100 entries
if (perfHistory.length > 100) {
perfHistory.shift();
}
lc.globals.set("perf_history", perfHistory);
// Calculate average
var total = perfHistory.reduce(function(sum, entry) {
return sum + entry.time;
}, 0);
var avg = Math.round(total / perfHistory.length);
console.log("Current: " + lc.response.time + "ms | Average: " + avg + "ms");
Authentication Testing
Login Response
lc.test("Login successful", function() {
lc.expect(lc.response.status).toBe(200);
});
var data = lc.response.body.json();
lc.test("Response contains access token", function() {
lc.expect(data).toHaveProperty("access_token");
lc.expect(data.access_token).toBeDefined();
});
lc.test("Response contains token expiry", function() {
lc.expect(data).toHaveProperty("expires_in");
lc.expect(data.expires_in).toBeGreaterThan(0);
});
lc.test("Token is valid JWT format", function() {
var parts = data.access_token.split(".");
lc.expect(parts.length).toBe(3);
});
// Store tokens for subsequent requests
if (data && data.access_token) {
lc.env.set("access_token", data.access_token);
console.log("Token stored successfully");
}
Protected Endpoint Access
lc.test("Protected endpoint returns data", function() {
lc.expect(lc.response.status).toBe(200);
});
lc.test("Response is not unauthorized", function() {
lc.expect(lc.response.status).not.toBe(401);
lc.expect(lc.response.status).not.toBe(403);
});
Data Validation Patterns
Email Format
lc.test("User email is valid format", function() {
var user = lc.response.body.json();
lc.expect(user.email).toMatch("@");
lc.expect(user.email).toContain(".");
});
UUID Format
lc.test("ID is valid UUID", function() {
var data = lc.response.body.json();
var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
lc.expect(data.id).toMatch(uuidRegex.source);
});
Date Format
lc.test("Created date is valid ISO format", function() {
var data = lc.response.body.json();
var date = new Date(data.createdAt);
lc.expect(isNaN(date.getTime())).toBe(false);
});
lc.test("Date is recent", function() {
var data = lc.response.body.json();
var created = new Date(data.createdAt);
var now = new Date();
var hourAgo = new Date(now.getTime() - 60 * 60 * 1000);
lc.expect(created.getTime()).toBeGreaterThan(hourAgo.getTime());
});
Numeric Ranges
var product = lc.response.body.json();
lc.test("Price is positive", function() {
lc.expect(product.price).toBeGreaterThan(0);
});
lc.test("Quantity is non-negative", function() {
lc.expect(product.quantity).toBeGreaterThanOrEqual(0);
});
lc.test("Rating is within range", function() {
lc.expect(product.rating).toBeGreaterThanOrEqual(0);
lc.expect(product.rating).toBeLessThanOrEqual(5);
});
Common Patterns
Conditional Tests
var data = lc.response.body.json();
// Only run test if condition is met
if (data && data.items && data.items.length > 0) {
lc.test("First item has required fields", function() {
var first = data.items[0];
lc.expect(first).toHaveProperty("id");
lc.expect(first).toHaveProperty("name");
});
}
Test Suite Pattern
// Organize tests into logical groups
function testStatusCode() {
lc.test("Status is 200", function() {
lc.expect(lc.response.status).toBe(200);
});
}
function testResponseTime() {
lc.test("Response under 2s", function() {
lc.expect(lc.response.time).toBeLessThan(2000);
});
}
function testResponseBody() {
var data = lc.response.body.json();
lc.test("Body is valid JSON", function() {
lc.expect(data).not.toBeNull();
});
lc.test("Body has required fields", function() {
lc.expect(data).toHaveProperty("id");
});
}
// Run all test groups
testStatusCode();
testResponseTime();
testResponseBody();
Reusable Assertions
// Helper function for common validations
function assertUserFields(user, testPrefix) {
testPrefix = testPrefix || "";
lc.test(testPrefix + "User has ID", function() {
lc.expect(user).toHaveProperty("id");
});
lc.test(testPrefix + "User has valid email", function() {
lc.expect(user.email).toMatch("@");
});
lc.test(testPrefix + "User has name", function() {
lc.expect(user.name).toBeDefined();
lc.expect(user.name.length).toBeGreaterThan(0);
});
}
// Use the helper
var data = lc.response.body.json();
if (data && data.user) {
assertUserFields(data.user, "Response: ");
}
Log and Test Together
// Combined logging and testing
var data = lc.response.body.json();
console.log("=== Test Results ===");
console.log("Status: " + lc.response.status);
console.log("Time: " + lc.response.time + "ms");
if (data) {
console.log("Items: " + (data.items ? data.items.length : 0));
console.log("Total: " + (data.total || "N/A"));
}
lc.test("Status is 200", function() {
lc.expect(lc.response.status).toBe(200);
});
lc.test("Has items", function() {
lc.expect(data.items.length).toBeGreaterThan(0);
});
console.log("====================");
See Also
- lc.test - Testing API reference
- lc.response - Response object
- Authentication Examples
- Request Chaining Examples