Skip to content

Chapter 17 · APIRequestContext and API Testing ​

Chapter goals: Use Playwright's built-in HTTP client APIRequestContext to test REST APIs directly, and combine "API seeding + UI verification" into the fastest test shape.

17.1 Why Not Just Use requests ​

Playwright ships a full HTTP client, APIRequestContext, with official use cases:

  • Test server APIs directly;
  • Prepare server-side state via API before visiting the web app;
  • Verify server-side post-conditions after browser operations.

Compared to requests/httpx, its unique advantage is same-origin sharing with the browser context: it automatically carries the same Cookies/auth state as the page (the storage_state from Chapter 9 is directly reusable), and cleans up uniformly with the context — the UI and API channels are natively打通.

17.2 Creating an Independent API Context ​

The官方 pattern: session-scoped fixture + playwright.request.new_context():

python
# conftest.py
import os
from typing import Generator
import pytest
from playwright.sync_api import Playwright, APIRequestContext

API_TOKEN = os.getenv("GITHUB_API_TOKEN", "")


@pytest.fixture(scope="session")
def api_request_context(
    playwright: Playwright,
) -> Generator[APIRequestContext, None, None]:
    headers = {
        # Declare version per GitHub spec
        "Accept": "application/vnd.github.v3+json",
        "Authorization": f"token {API_TOKEN}",
    }
    request_context = playwright.request.new_context(
        base_url="https://api.github.com",
        extra_http_headers=headers,
    )
    yield request_context
    request_context.dispose()   # Clean up connections uniformly

base_url lets all subsequent requests use relative paths; get/post/put/delete methods return APIResponse with useful attributes: .ok (2xx check), .status, .json(), .text().

17.3 Writing a Set of API Cases ​

Skeleton from the official GitHub Issues example:

python
# tests/test_issues_api.py
GITHUB_USER = "your-user"
GITHUB_REPO = "api-test-repo"


def test_should_create_bug_report(api_request_context: APIRequestContext):
    data = {"title": "[Bug] report 1", "body": "Bug description"}
    new_issue = api_request_context.post(
        f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues", data=data
    )
    assert new_issue.ok                       # Assert 2xx

    issues = api_request_context.get(f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues")
    assert issues.ok
    issue = [i for i in issues.json() if i["title"] == "[Bug] report 1"][0]
    assert issue["body"] == "Bug description"

Pair with session-scoped setup/teardown to manage the existence of "the resource under test" (create the repo first, delete after):

python
@pytest.fixture(scope="session", autouse=True)
def create_test_repository(api_request_context: APIRequestContext):
    new_repo = api_request_context.post("/user/repos", data={"name": GITHUB_REPO})
    assert new_repo.ok
    yield                                     # ---- all tests run between here ----
    deleted = api_request_context.delete(f"/repos/{GITHUB_USER}/{GITHUB_REPO}")
    assert deleted.ok

17.4 API Seeding + UI Verification: The Fastest Shape ​

The most efficient E2E pattern: seed data via API (fast, deterministic), verify via UI (human-validated):

python
def test_product_visible_after_api_create(page, api_request_context):
    # Fast API seed
    resp = api_request_context.post("/api/products", json={"name": "Widget", "price": 9.9})
    product = resp.json()

    # UI verification
    page.goto(f"/products/{product['id']}")
    expect(page.get_by_role("heading", name="Widget")).to_be_visible()
    expect(page.get_by_text("$9.90")).to_be_visible()

This cuts test time by 60–80% compared to UI-only data creation, while keeping the human-meaningful UI assertion.

17.5 Chapter Summary ​

  • APIRequestContext is Playwright's built-in HTTP client; shares auth state with the browser;
  • Session-scoped fixture + dispose() is the standard lifecycle pattern;
  • API seeding + UI verification is the fastest E2E shape;
  • Combine with storage_state (Ch 9) for authenticated API calls.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. What is the key advantage of APIRequestContext over requests.http?

2. What does APIResponse.ok represent?

3. Why is API seeding + UI verification faster than UI-only data creation?

4. When should you call request_context.dispose()?

🛠️ Hands-On Exercises ​

  1. Write a session-scoped api_request_context fixture for a public REST API (e.g., JSONPlaceholder); create a resource via POST and assert its ID in the response.
  2. Combine API seeding with a UI test: create a product via API, visit the storefront, and assert the product appears with correct price.
  3. Add a session-scoped teardown that deletes the created resource via DELETE; verify it no longer appears in a subsequent GET.

Proceed to Chapter 18: Async API and FastAPI Integration Testing.