Playwright API Testing
APIRequestContext โ HTTP Requests
import { test, expect } from '@playwright/test';
// Use built-in `request` fixture
test('GET /api/users', async ({ request }) => {
const response = await request.get('/api/users');
expect(response.status()).toBe(200);
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(body).toHaveLength(3);
expect(body[0]).toMatchObject({ id: expect.any(Number), name: expect.any(String) });
});
test('POST /api/users', async ({ request }) => {
const response = await request.post('/api/users', {
data: { name: 'Alice', email: '[email protected]' },
headers: { 'Content-Type': 'application/json' },
});
expect(response.status()).toBe(201);
const user = await response.json();
expect(user.id).toBeDefined();
});
test('DELETE /api/users/:id', async ({ request }) => {
const del = await request.delete('/api/users/1');
expect(del.status()).toBe(204);
});
Standalone APIRequestContext
import { request } from '@playwright/test';
// Create context outside of test (e.g., global setup)
const context = await request.newContext({
baseURL: 'https://api.example.com',
extraHTTPHeaders: {
'Authorization': 'Bearer my-token',
'Accept': 'application/json',
},
});
const response = await context.get('/users');
const users = await response.json();
console.log(users);
await context.dispose();
// In playwright.config.ts
export default defineConfig({
use: {
baseURL: 'http://localhost:3000',
extraHTTPHeaders: {
'x-api-key': process.env.API_KEY,
},
},
});
Response Assertions
test('API response assertions', async ({ request }) => {
const response = await request.get('/api/profile');
// Status
expect(response.status()).toBe(200);
expect(response.ok()).toBeTruthy(); // 200โ299
// Headers
expect(response.headers()['content-type']).toContain('application/json');
// Body as JSON
const json = await response.json();
expect(json).toMatchObject({
id: expect.any(Number),
email: expect.stringContaining('@'),
});
// Body as text
const text = await response.text();
expect(text).toContain('"status":"ok"');
// Body as buffer (for binary)
const buffer = await response.body();
expect(buffer.byteLength).toBeGreaterThan(0);
// Playwright built-in API assertions
await expect(response).toBeOK();
});
Auth Storage โ storageState
// global-setup.ts โ login once, reuse auth across all tests
import { chromium } from '@playwright/test';
async function globalSetup() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('http://localhost:3000/login');
await page.fill('#email', '[email protected]');
await page.fill('#password', 'adminpass');
await page.click('[type="submit"]');
await page.waitForURL('**/dashboard');
// Save cookies + localStorage
await page.context().storageState({ path: 'auth/admin.json' });
await browser.close();
}
export default globalSetup;
// playwright.config.ts
export default defineConfig({
globalSetup: './global-setup.ts',
use: {
storageState: 'auth/admin.json',
},
});
// API context with saved auth
test('authenticated API call', async ({ request }) => {
// cookies from storageState are automatically included
const response = await request.get('/api/admin/stats');
expect(response.status()).toBe(200);
});
Route Mocking โ page.route()
test('mock API responses', async ({ page }) => {
// Intercept and mock
await page.route('**/api/users', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, name: 'Alice' }]),
});
});
// Intercept and modify
await page.route('**/api/products', async route => {
const response = await route.fetch();
const body = await response.json();
body.featured = true; // add field
await route.fulfill({ response, body: JSON.stringify(body) });
});
// Abort a request
await page.route('**/*.png', route => route.abort());
await page.goto('/');
await expect(page.getByText('Alice')).toBeVisible();
});
// Reuse route in multiple tests
test.beforeEach(async ({ page }) => {
await page.route('**/api/**', async route => {
if (route.request().url().includes('/auth')) return route.continue();
await route.fulfill({ body: '{}', contentType: 'application/json' });
});
});
API Testing Quick Reference
| Method | Purpose |
|---|---|
request.get(url) | HTTP GET request |
request.post(url, {data}) | HTTP POST with JSON body |
request.put(url, {data}) | HTTP PUT request |
request.patch(url, {data}) | HTTP PATCH request |
request.delete(url) | HTTP DELETE request |
response.json() | Parse response as JSON |
response.status() | Get HTTP status code |
response.ok() | True if status 200-299 |
page.route(pattern, handler) | Intercept/mock network |
storageState | Save/restore auth cookies |