Implementation:Microsoft Playwright APIRequestContext Dispose
| Knowledge Sources | |
|---|---|
| Domains | API_Testing, HTTP, Test_Teardown |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Concrete tool for cleaning up test-created resources via HTTP DELETE requests and disposing of HTTP client contexts after tests complete, provided by the Playwright library.
Description
Playwright provides multiple mechanisms for test teardown and resource cleanup:
test.afterAll() Hook
The afterAll hook (defined at packages/playwright/types/test.d.ts:L6121-6162) runs once after all tests in a file or describe block have completed. It receives the request fixture, allowing tests to make API calls to clean up server-side resources. This hook always runs, even when tests fail, ensuring cleanup is not skipped.
request.delete() Method
The delete() method sends an HTTP DELETE request to remove resources created during test setup. It follows the same pattern as other request methods, accepting a URL and optional configuration.
context.dispose() Method
The dispose() method (implemented at packages/playwright-core/src/client/fetch.ts:L110-129) releases all resources held by the APIRequestContext:
- On the server side (
packages/playwright-core/src/server/fetch.ts:L654-659), disposal flushes any active tracing artifacts and clears the stored response bodies from memory. - Once disposed, the context can no longer be used to make requests.
- The method accepts an optional
{ reason?: string }parameter to document why the context was disposed.
Symbol.asyncDispose Support
The APIRequestContext implements Symbol.asyncDispose (at packages/playwright-core/src/client/fetch.ts:L106-108), enabling automatic disposal when used with the await using syntax in TypeScript 5.2+.
Usage
Use test.afterAll() with request.delete() to remove test-created resources from the server. Call dispose() on any manually created APIRequestContext instances (those created via APIRequest.newContext()). The built-in request fixture is automatically disposed by Playwright and does not require manual disposal.
Code Reference
Source Location
- Repository: playwright
- afterAll hook:
packages/playwright/types/test.d.ts:L6121-6162 - Client dispose:
packages/playwright-core/src/client/fetch.ts:L110-129 - Server dispose:
packages/playwright-core/src/server/fetch.ts:L654-659 - Symbol.asyncDispose:
packages/playwright-core/src/client/fetch.ts:L106-108
Signature
// Test teardown hook
test.afterAll(
inner: (args: { request: APIRequestContext }, testInfo: TestInfo) => Promise<any>
): void;
// HTTP DELETE request
request.delete(url: string, options?: {
data?: string | Buffer | Serializable;
form?: { [key: string]: string | number | boolean };
headers?: { [key: string]: string };
params?: { [key: string]: string | number | boolean };
timeout?: number;
failOnStatusCode?: boolean;
ignoreHTTPSErrors?: boolean;
maxRedirects?: number;
maxRetries?: number;
}): Promise<APIResponse>;
// Context disposal
context.dispose(options?: {
reason?: string;
}): Promise<void>;
Import
import { test, expect } from '@playwright/test';
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| afterAll callback | (args: { request: APIRequestContext }, testInfo: TestInfo) => Promise<any> |
Yes | An async function that receives the request fixture and testInfo. Executes after all tests in the current scope have completed, regardless of pass/fail status.
|
| delete: url | string |
Yes | The URL of the resource to delete. Relative URLs are resolved against the context's baseURL.
|
| delete: options | object |
No | Optional request configuration including headers, timeout, body data (for DELETE requests that require a body), and other standard request options. |
| dispose: options.reason | string |
No | An optional human-readable reason for disposing the context. Logged for debugging purposes. |
Outputs
| Name | Type | Description |
|---|---|---|
| delete() return | Promise<APIResponse> |
The API response from the DELETE request. Check response.ok() to verify the deletion succeeded. A 204 No Content status typically indicates successful deletion.
|
| dispose() return | Promise<void> |
Resolves when the context has been fully disposed. Server-side tracing is flushed and all cached response bodies are cleared from memory. |
Usage Examples
Basic Example
import { test, expect } from '@playwright/test';
test.use({
baseURL: 'https://api.example.com',
extraHTTPHeaders: {
'Authorization': 'Bearer my-token',
},
});
let createdUserId: number;
test.beforeAll(async ({ request }) => {
// Setup: create a test user
const response = await request.post('/api/users', {
data: {
username: 'cleanup-test-user',
email: 'cleanup@example.com',
},
});
expect(response.ok()).toBeTruthy();
const body = await response.json();
createdUserId = body.id;
});
test('verify user was created', async ({ request }) => {
const response = await request.get(`/api/users/${createdUserId}`);
expect(response.ok()).toBeTruthy();
});
test.afterAll(async ({ request }) => {
// Teardown: delete the test-created user
const deleteResponse = await request.delete(`/api/users/${createdUserId}`);
expect(deleteResponse.ok()).toBeTruthy();
});
Example: Manual Context Disposal
import { test, request as playwrightRequest } from '@playwright/test';
test('create and dispose manual context', async () => {
const context = await playwrightRequest.newContext({
baseURL: 'https://api.example.com',
});
try {
const response = await context.get('/api/health');
expect(response.ok()).toBeTruthy();
} finally {
// Always dispose the context, even if the test fails
await context.dispose({ reason: 'Test completed' });
}
});
Example: Using Symbol.asyncDispose
import { test, request as playwrightRequest } from '@playwright/test';
test('automatic disposal with await using', async () => {
// TypeScript 5.2+ syntax: context is automatically disposed
// when it goes out of scope
await using context = await playwrightRequest.newContext({
baseURL: 'https://api.example.com',
});
const response = await context.get('/api/health');
expect(response.ok()).toBeTruthy();
// No manual dispose() call needed -- Symbol.asyncDispose handles it
});
Example: Comprehensive Setup and Teardown
import { test, expect } from '@playwright/test';
test.use({
baseURL: 'https://api.example.com',
extraHTTPHeaders: {
'Authorization': 'Bearer admin-token',
},
});
const createdResources: { type: string; id: number }[] = [];
test.beforeAll(async ({ request }) => {
// Create multiple test resources
const userResponse = await request.post('/api/users', {
data: { username: 'test-user', role: 'editor' },
});
const user = await userResponse.json();
createdResources.push({ type: 'users', id: user.id });
const articleResponse = await request.post('/api/articles', {
data: { title: 'Test Article', authorId: user.id },
});
const article = await articleResponse.json();
createdResources.push({ type: 'articles', id: article.id });
});
test('tests run here...', async ({ request }) => {
// Test logic
});
test.afterAll(async ({ request }) => {
// Clean up in reverse order to respect dependencies
for (const resource of createdResources.reverse()) {
const response = await request.delete(
`/api/${resource.type}/${resource.id}`
);
// Tolerate 404 in case a test already deleted the resource
if (response.status() !== 404) {
expect(response.ok()).toBeTruthy();
}
}
});