IntermediateSDET

Mock external APIs in tests with respx and Playwright route()

Build a test suite for a service that calls two external APIs. Using respx (for Python/httpx tests) and Playwright's page.route() (for browser tests), mock both APIs so tests run without network access, cover error and timeout scenarios impossible to trigger against real APIs, and measure the speed improvement.

Why this matters

Tests that call real external APIs are slow, flaky, and coupled to rate limits you do not control. Mocking at the HTTP boundary; not at the function boundary; gives you isolation without losing realism: your code actually makes the HTTP call, the mock just intercepts it. This distinction matters because most real integration bugs are in the request construction or response parsing, not the business logic.

Before you start

Step-by-step guide

  1. 1

    Write a test that calls a real API and measure it

    Write a pytest test that calls a real external API. Record the time taken. Run it 10 times and note the variance. This is your baseline: slow, variable, and broken when the API is down.

  2. 2

    Mock with respx

    Use the @respx.mock decorator (or respx.mock context manager). Define the mock route: respx.get('https://api.example.com/endpoint').mock(return_value=httpx.Response(200, json={...})). Run the test; it should pass without any network call and complete in under 10ms.

  3. 3

    Test error scenarios

    Define mocks that return 429 (rate limit), 500 (server error), and a network timeout (side_effect=httpx.ConnectTimeout). Write tests that verify your code handles each gracefully. These scenarios are impossible to trigger reliably against a real API.

  4. 4

    Mock in Playwright with page.route()

    In a Playwright test, use await page.route('**/api/external/**', route => route.fulfill({ status: 200, body: JSON.stringify({...}) })). Load the page and verify the UI renders correctly. Then mock a 500 and verify the error state renders correctly.

  5. 5

    Test the timeout state in the browser

    Use route => route.fulfill({ delay: 5000, status: 200 }) to simulate a slow API. Verify the UI shows a loading state and eventually a timeout error. This test is only possible with network interception; you cannot reliably slow down a real API.

  6. 6

    Compare test execution time

    Run your full suite with real network calls enabled. Then run with mocking. For a suite of 20 tests hitting 2 external APIs, the difference is typically 30-120 seconds vs under 5 seconds. That is the CI cost of untested external dependencies.

Relevant Axiom pages

What to do next

Back to Practice Lab