Testing Examples
This guide provides practical examples of testing with Balda.js MockServer, covering common scenarios and patterns.
Basic API Testing
Simple GET Request
import { describe, it, expect } from "vitest";
import { mockServer } from "test/server/instance";
describe("Basic API Tests", () => {
it("GET /users returns user list", async () => {
const res = await mockServer.get("/users");
expect(res.statusCode()).toBe(200);
expect(Array.isArray(res.body())).toBe(true);
expect(res.body().length).toBeGreaterThan(0);
});
});
GET with Query Parameters
it("GET /users with pagination", async () => {
const res = await mockServer.get("/users", {
query: { page: "1", limit: "5" }
});
expect(res.statusCode()).toBe(200);
expect(res.body().length).toBeLessThanOrEqual(5);
});
GET with Headers
it("GET /protected with authorization", async () => {
const res = await mockServer.get("/protected", {
headers: { "Authorization": "Bearer token123" }
});
expect(res.statusCode()).toBe(200);
});
POST Request Testing
JSON Body
it("POST /users creates new user", async () => {
const newUser = {
name: "John Doe",
email: "john@example.com",
age: 30
};
const res = await mockServer.post("/users", { body: newUser });
expect(res.statusCode()).toBe(201);
expect(res.assertBodyDeepEqual(newUser));
});
Form Data (URL Encoded)
it("POST /users with form data", async () => {
const res = await mockServer.post("/users", {
urlencoded: {
name: "Jane Doe",
email: "jane@example.com",
age: "25"
}
});
expect(res.statusCode()).toBe(201);
expect(res.assertBodySubset({ name: "Jane Doe" }));
});
File Upload
it("POST /upload handles file upload", async () => {
const formData = new FormData();
const fileContent = new Uint8Array([1, 2, 3, 4, 5]);
formData.append("file", new Blob([fileContent]), "test.txt");
const res = await mockServer.post("/upload", { formData });
expect(res.statusCode()).toBe(200);
expect(res.body()).toEqual({
originalName: "test.txt",
filename: "file",
size: 5,
mimetype: "application/octet-stream"
});
});
Error Handling Examples
404 Not Found
it("GET /users/999 returns 404", async () => {
const res = await mockServer.get("/users/999");
expect(res.statusCode()).toBe(404);
expect(res.assertBodyDeepEqual({ error: "User not found" }));
});
409 Conflict
it("POST /users returns 409 for existing user", async () => {
const existingUser = {
id: 1,
name: "John Doe",
email: "john.doe@example.com",
age: 20
};
const res = await mockServer.post("/users", { body: existingUser });
expect(res.statusCode()).toBe(409);
expect(res.assertBodyDeepEqual({ error: "User already exists" }));
});
Server Error
it("GET /users with shouldFail=true returns 500", async () => {
const res = await mockServer.get("/users", {
query: { shouldFail: "true" }
});
expect(res.statusCode()).toBe(500);
});
CRUD Operations Testing
Complete CRUD Test Suite
describe("User CRUD Operations", () => {
it("creates a new user", async () => {
const newUser = { id: 3, name: "New User", email: "new@example.com", age: 30 };
const res = await mockServer.post("/users", { body: newUser });
expect(res.statusCode()).toBe(201);
expect(res.assertBodyDeepEqual(newUser));
});
it("reads a specific user", async () => {
const res = await mockServer.get("/users/1");
expect(res.statusCode()).toBe(200);
expect(res.assertBodySubset({ id: 1 }));
});
it("updates a user", async () => {
const res = await mockServer.patch("/users/1", {
body: { name: "Updated Name" }
});
expect(res.statusCode()).toBe(200);
expect(res.assertBodySubset({ id: 1, name: "Updated Name" }));
});
it("deletes a user", async () => {
const res = await mockServer.delete("/users/1");
expect(res.statusCode()).toBe(204);
});
});
Authentication Testing
Protected Routes
describe("Authentication", () => {
it("accesses protected route with valid token", async () => {
const res = await mockServer.get("/protected", {
headers: { "Authorization": "Bearer valid-token" }
});
expect(res.statusCode()).toBe(200);
});
it("rejects access without token", async () => {
const res = await mockServer.get("/protected");
expect(res.statusCode()).toBe(401);
});
it("rejects access with invalid token", async () => {
const res = await mockServer.get("/protected", {
headers: { "Authorization": "Bearer invalid-token" }
});
expect(res.statusCode()).toBe(401);
});
});
Cookie Testing
Setting and Reading Cookies
describe("Cookie Handling", () => {
it("sets session cookie on login", async () => {
const res = await mockServer.post("/login", {
body: { username: "user", password: "pass" }
});
expect(res.statusCode()).toBe(200);
expect(res.headers()["set-cookie"]).toContain("sessionId");
});
it("uses existing session cookie", async () => {
const res = await mockServer.get("/profile", {
cookies: { sessionId: "valid-session" }
});
expect(res.statusCode()).toBe(200);
});
});
Advanced Testing Patterns
Testing with Custom Headers
it("handles custom headers", async () => {
const res = await mockServer.get("/api/data", {
headers: {
"X-API-Key": "secret-key",
"X-Client-Version": "1.0.0",
"Content-Type": "application/json"
}
});
expect(res.statusCode()).toBe(200);
});
Testing with IP Address
it("handles IP-based logic", async () => {
const res = await mockServer.get("/location", {
ip: "192.168.1.100"
});
expect(res.statusCode()).toBe(200);
expect(res.body()).toHaveProperty("ip", "192.168.1.100");
});
Testing Query Parameters
it("handles complex query parameters", async () => {
const res = await mockServer.get("/search", {
query: {
q: "search term",
category: "technology",
sort: "date",
order: "desc",
page: "1",
limit: "20"
}
});
expect(res.statusCode()).toBe(200);
expect(res.body()).toHaveProperty("results");
});
Testing Middleware
Rate Limiting
describe("Rate Limiting", () => {
it("allows requests within limit", async () => {
const res = await mockServer.get("/api/limited");
expect(res.statusCode()).toBe(200);
});
it("blocks requests over limit", async () => {
// Simulate multiple requests
for (let i = 0; i < 10; i++) {
await mockServer.get("/api/limited");
}
const res = await mockServer.get("/api/limited");
expect(res.statusCode()).toBe(429);
});
});
CORS Testing
describe("CORS", () => {
it("handles preflight OPTIONS request", async () => {
const res = await mockServer.request("OPTIONS", "/api/data", {
headers: {
"Origin": "https://example.com",
"Access-Control-Request-Method": "POST"
}
});
expect(res.statusCode()).toBe(200);
expect(res.headers()["access-control-allow-origin"]).toBe("*");
});
});
Performance Testing
Load Testing with MockServer
describe("Performance", () => {
it("handles multiple concurrent requests", async () => {
const promises = Array.from({ length: 100 }, () =>
mockServer.get("/api/data")
);
const results = await Promise.all(promises);
results.forEach(res => {
expect(res.statusCode()).toBe(200);
});
});
});
Best Practices
Test Organization
describe("User API", () => {
describe("GET /users", () => {
it("returns all users", async () => {
// Test implementation
});
it("supports pagination", async () => {
// Test implementation
});
});
describe("POST /users", () => {
it("creates new user", async () => {
// Test implementation
});
it("validates required fields", async () => {
// Test implementation
});
});
});
Type Safety
interface User {
id: number;
name: string;
email: string;
age: number;
}
describe("Type-safe testing", () => {
it("returns properly typed user data", async () => {
const res = await mockServer.get<User[]>("/users");
const users = res.body() as User[];
expect(users[0]).toHaveProperty("id");
expect(users[0]).toHaveProperty("name");
expect(users[0]).toHaveProperty("email");
});
});
These examples demonstrate the flexibility and power of MockServer for comprehensive API testing in Balda.js applications.