Skip to main content

Overview

API testing validates backend functionality without a browser. Playwright includes powerful API testing capabilities that let you test REST APIs, verify responses, and validate data structures.

Setting Up API Context

Before making API requests, create an API context with your base URL and configuration:
conftest.py (Lines 5-11)
import pytest
from playwright.sync_api import Playwright, APIRequestContext

@pytest.fixture
def api_context(playwright: Playwright) -> APIRequestContext:
    api_context = playwright.request.new_context(
        base_url="https://dummyjson.com"
    )
    yield api_context
    api_context.dispose()
1

Create fixture

Define a pytest fixture that provides the API context to your tests
2

Configure base URL

Set the base_url so you can use relative paths in requests
3

Yield context

Use yield to provide the context to tests
4

Cleanup

Call dispose() to clean up resources after tests complete
The @pytest.fixture decorator makes the API context available to any test that includes api_context as a parameter.

Making GET Requests

GET requests retrieve data from your API. Here’s a complete example:
test_API.py (Lines 14-24)
def test_users_search(api_context: APIRequestContext):
    query = "Emily"
    response = api_context.get(f"/users/search?q={query}")

    users_data = response.json()

    print("Users found:", users_data["total"])

    for user in users_data["users"]:
        print("Checking user:", user["firstName"])
        assert query in user["firstName"]
response = api_context.get("/users")
data = response.json()

Making POST Requests

POST requests create new resources. Include data in the request body:
test_API.py (Lines 26-36)
def test_create_user(api_context: APIRequestContext):
    response = api_context.post(
        "users/add",
        headers={"Content-Type": "application/json"},
        data={"firstName": "Damien", "lastName": "Smith", "age": 27}
    )

    user_data = response.json()
    assert user_data["id"] == 209
    assert user_data["firstName"] == "Damien"
    print(f"\n {user_data}")
GET
method
Retrieve data without modifying it. Use for reading resources.
POST
method
Create new resources. Send data in the request body.
PUT
method
Update entire resources. Replaces all fields.
PATCH
method
Partially update resources. Only modifies specified fields.
DELETE
method
Remove resources permanently.

Validating Responses

Always validate both the response status and data structure:
# Check status code
assert response.status == 200
assert response.ok  # True for 2xx status codes

# Test error cases
response = api_context.get("/invalid-endpoint")
assert response.status == 404

Working with Response Data

Parse and validate complex API responses:
test_API.py (Lines 38-42)
def test_count_users(api_context: APIRequestContext):
    response = api_context.get("/users")
    user_data = response.json()

    print(f"\n Total users : {user_data['total']}")
# Parse JSON
data = response.json()

# Access nested data
total = data["total"]
users = data["users"]

# Iterate through arrays
for user in data["users"]:
    print(user["firstName"])

Complete API Test Examples

Testing Search Functionality

def test_users_search(api_context: APIRequestContext):
    """Test that search returns only matching users"""
    query = "Emily"
    response = api_context.get(f"/users/search?q={query}")
    
    # Verify successful response
    assert response.ok
    
    # Parse and validate data
    users_data = response.json()
    assert users_data["total"] > 0
    
    # Verify all results match search query
    for user in users_data["users"]:
        assert query in user["firstName"]

Testing Resource Creation

def test_create_user(api_context: APIRequestContext):
    """Test creating a new user via POST"""
    new_user = {
        "firstName": "Damien",
        "lastName": "Smith",
        "age": 27
    }
    
    response = api_context.post(
        "users/add",
        headers={"Content-Type": "application/json"},
        data=new_user
    )
    
    # Verify creation was successful
    assert response.ok
    
    # Validate returned user data
    user_data = response.json()
    assert user_data["id"] is not None
    assert user_data["firstName"] == new_user["firstName"]
    assert user_data["lastName"] == new_user["lastName"]
    assert user_data["age"] == new_user["age"]
Important: Many test APIs like DummyJSON simulate operations but don’t persist data. Always check API documentation to understand behavior.

Authentication in API Tests

Handle authentication tokens and headers:
@pytest.fixture
def authenticated_api_context(playwright: Playwright):
    token = "your-auth-token"
    api_context = playwright.request.new_context(
        base_url="https://api.example.com",
        extra_http_headers={
            "Authorization": f"Bearer {token}"
        }
    )
    yield api_context
    api_context.dispose()

Error Handling

Test how your API handles errors:
def test_invalid_endpoint(api_context: APIRequestContext):
    """Test that invalid endpoints return 404"""
    response = api_context.get("/nonexistent")
    assert response.status == 404

def test_invalid_data(api_context: APIRequestContext):
    """Test that invalid data is rejected"""
    response = api_context.post(
        "users/add",
        data={"invalid": "data"}
    )
    assert response.status == 400  # Bad Request

def test_unauthorized_access(api_context: APIRequestContext):
    """Test that protected endpoints require authentication"""
    response = api_context.get("/protected-resource")
    assert response.status == 401  # Unauthorized

Best Practices

Use Fixtures

Create reusable fixtures for API contexts to avoid repetition

Test All Status Codes

Verify both success (2xx) and error (4xx, 5xx) responses

Validate Data Structure

Check that responses contain expected fields and types

Clean Up Resources

Delete test data created during API tests
Pro Tip: Combine API and UI tests. Use API calls to set up test data quickly, then verify the UI displays it correctly.

Debugging API Tests

# Print response details for debugging
response = api_context.get("/users")
print(f"Status: {response.status}")
print(f"Headers: {response.headers}")
print(f"Body: {response.text()}")

# Pretty print JSON responses
import json
data = response.json()
print(json.dumps(data, indent=2))

Next Steps

UI Testing

Learn browser-based UI testing

Assertions

Master assertion techniques

Parametrization

Test with multiple data sets

Fixtures

Deep dive into pytest fixtures

Build docs developers (and LLMs) love