How to Add a New API Test Case
How to Add a New API Test Case¶
๐ 1. Create or Extend an API Controller¶
All API logic is abstracted under the controllers/ folder. This helps separate test logic from API interaction and makes your code more reusable and testable.
๐ง Example: Add a new GET endpoint¶
๐ controllers/documents/getDocument.ts
import { getSharedApiContext } from "../../utils/functions/apiContext";
export async function getDocument(documentId?: string) {
const id = documentId || process.env.DOCUMENT_ID;
const apiContext = await getSharedApiContext();
return apiContext.get(`/api/documents/${id}`);
}
export async function getAllDocuments() {
const apiContext = await getSharedApiContext();
return apiContext.get(`/api/documents`);
}
โ
Place related endpoints in the same file (e.g., /documents calls in one file).
๐งช 2. Add the Corresponding Test Spec¶
All API test cases go in tests/api/.
๐งพ Template Structure¶
import { test, expect } from "@playwright/test";
import { getDocument } from "../../controllers/documents/getDocument";
test.describe("Document API Tests", () => {
test("Validate get document by ID @api", async () => {
const response = await getDocument("sample-document-id");
expect(response.status()).toBe(200);
const body = await response.json();
expect(body).toHaveProperty("documentId");
});
});
๐งผ 3. Use beforeEach and afterEach for Setup and Cleanup¶
Use beforeEach to prepare the environment (e.g., create a document), and afterEach to clean up.
test.beforeEach(async () => {
const response = await createDocument("pdf", "MyFile.pdf");
const body = await response.json();
documentId = body.documentId;
});
test.afterEach(async () => {
await deleteDocumentById(documentId);
});
๐งฑ 4. Create Step-Based Assertions¶
Use test.step() blocks for detailed traceability:
test("Create a spell check execution @api", async () => {
await test.step("Send request to create execution", async () => {
const response = await createSpellCheckExecution(documentId);
expect(response.status()).toBe(201);
const body = await response.json();
expect(body).toHaveProperty("id");
});
});
๐งช 5. Tag and Execute the Test¶
Add @api to your test description: test("Validate API response @api", async () => { ... }); Then run with: npx playwright test -g "@api"
๐งพ 6. Example: Full Lifecycle Test Case¶
test.describe("Spell Check API Tests", () => {
let documentId: string;
let spellCheckExecutionId: string;
test.beforeEach(async () => {
const res = await createDocument("pdf", "MyTestFile.pdf");
const body = await res.json();
documentId = body.documentId;
});
test("Create a spell check execution @api", async () => {
const res = await createSpellCheckExecution(documentId);
expect(res.status()).toBe(201);
const body = await res.json();
spellCheckExecutionId = body.id;
});
test.afterEach(async () => {
if (spellCheckExecutionId) {
await deleteSpellCheckExecution(documentId, spellCheckExecutionId);
}
await deleteDocumentById(documentId);
});
});
๐ Best Practices
| Practice | Why It Matters |
|---|---|
| Use controllers | Keeps test files clean and promotes reusability |
| Always assert response status | Ensures basic validation of HTTP call |
Use beforeEach / afterEach | Avoids test pollution and ensures isolation |
Add @api tag | For targeted test execution and reporting |
Use test.step() | Improves trace readability |
Log with console.log() or expect.soft() | For non-blocking insights during test runs |