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:
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()
Create fixture
Define a pytest fixture that provides the API context to your tests
Configure base URL
Set the base_url so you can use relative paths in requests
Yield context
Use yield to provide the context to tests
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" ]
Simple GET
GET with Query Parameters
GET with Headers
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 } " )
Request Methods Explained
Retrieve data without modifying it. Use for reading resources.
Create new resources. Send data in the request body.
Update entire resources. Replaces all fields.
Partially update resources. Only modifies specified fields.
Remove resources permanently.
Validating Responses
Always validate both the response status and data structure:
Status Codes
Response Body
Response Headers
# 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' ] } " )
JSON Responses
Text Responses
Binary Responses
# 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" ])
# Get response as text
text = response.text()
# Check for specific content
assert "Success" in text
# Download files or binary data
body = response.body()
# Save to file
with open ( "download.pdf" , "wb" ) as f:
f.write(body)
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:
Bearer Token
Basic Auth
API Key
@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