Mocking API Responses with Playwright: Success, Errors, Latency, Pagination, and Conditional Logic
This article demonstrates how to use Playwright to mock various API responses—including successful login, dynamic data, network latency, pagination, HTTP errors, redirects, authentication failures, custom headers, not‑found resources, and conditional responses—by defining route handlers that fulfill requests with custom status codes, bodies, and headers.
Mock login success
Scenario: In login testing, simulate a successful server response by returning a fake JWT token without calling the real API.
def login_success_mock(route):
if "/login" in route.request().url and route.request().method == 'POST':
route.fulfill(
status=200,
headers={"Content-Type": "application/json"},
body=json.dumps({"token": "mocked_token"})
)
else:
route.continue_()
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_page()
context.route("**/api/login*", login_success_mock)
# login operation...Dynamic API response
Scenario: Generate different responses based on request parameters to simulate multiple API interaction scenarios.
def dynamic_response(route):
params = parse_qs(urlparse(route.request().url).query)
if params.get('type') == ['success']:
route.fulfill(status=200, body=json.dumps({"status": "success", "data": "mocked_data"}))
else:
route.fulfill(status=400, body=json.dumps({"status": "error", "message": "Invalid request"}))
context.route("**/api/dynamic*", dynamic_response)Simulate network latency
Scenario: Test application behavior under slow network conditions by adding a three‑second delay before responding.
import time
def latency(route):
time.sleep(3) # simulate 3‑second delay
route.fulfill(status=200, body='{"response": "delayed"}')
context.route("**/api/slow*", latency)Simulate paginated data
Scenario: In pagination testing, return different data sets for each page number.
def paginated_response(route):
page_number = int(route.request().url.split('/')[-1])
mock_data = [{"id": i} for i in range(page_number*10, page_number*10 + 10)]
route.fulfill(status=200, body=json.dumps({"page": page_number, "items": mock_data}))
context.route("**/api/items/page/*", paginated_response)Simulate HTTP error
Scenario: Verify application handling of server errors such as 500 Internal Server Error.
def http_error(route):
route.fulfill(status=500, body='{"message": "Internal Server Error"}')
context.route("**/api/error*", http_error)Simulate redirect
Scenario: Mock a 302 redirect from one URL to another.
def redirect(route):
route.fulfill(status=302, headers={"Location": "http://example.com/mock-redirect-target"})
context.route("**/api/redirect*", redirect)Simulate authorization failure
Scenario: Mock an authentication failure by returning a 401 Unauthorized response.
def auth_failure(route):
route.fulfill(status=401, body=json.dumps({"message": "Unauthorized"}))
context.route("**/api/auth*", auth_failure)Simulate custom HTTP headers
Scenario: Add or modify specific HTTP headers in the mocked response.
def custom_headers(route):
route.fulfill(status=200, headers={"X-Mock-Header": "mocked_value"}, body='{}')
context.route("**/api/headers*", custom_headers)Simulate resource not found
Scenario: Mock a 404 Not Found error for missing resources.
def not_found(route):
route.fulfill(status=404, body='{"message": "Resource not found"}')
context.route("**/api/nonexistent*", not_found)Conditional response based on request body
Scenario: Dynamically decide the mocked response by inspecting the POST request payload.
def conditionally_mocked_response(route):
request_body = json.loads((await route.request().post_data_buffer()).decode())
if request_body["key"] == "mock":
route.fulfill(status=200, body=json.dumps({"mocked": True}))
else:
route.continue_()
context.route("**/api/conditional*", conditionally_mocked_response)Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.