QA Interview Hub · Knowledge base · Quizzes

Networking

Networking in QA work

DevTools Network, HAR, throttling and offline

The Network tab is the main analysis tool. What to look at:

  • filters (Fetch/XHR, WS, Doc), search through response bodies, Preserve log (so redirects and navigation do not wipe the list), Disable cache;
  • the columns Status, Protocol (h2, h3), Initiator (who created the request), Time;
  • a request's Timing: Queueing/Stalled (waiting for a slot), DNS Lookup, Initial connection, SSL, Request sent, Waiting (TTFB), Content Download. A long Waiting means a slow server; long DNS/SSL means the network or a new connection; a long Stalled means too many requests per domain;
  • For WebSocket, the Messages tab with frames in both directions.

HAR (HTTP Archive) is a JSON record of all network activity. "Save all as HAR" is attached to a bug instead of a screenshot; but a HAR contains cookies, tokens and response bodies, so it is sanitized before sharing. In Playwright the recordHar context option records a HAR during a test, and page.routeFromHAR(path) replays it instead of the real network (the update: true mode refreshes the recording).

Slow network. DevTools has Fast/Slow 4G, Offline and custom profiles. Playwright has no built-in throttling: for Chromium use CDP:

const cdp = await context.newCDPSession(page);
await cdp.send('Network.enable');
await cdp.send('Network.emulateNetworkConditions', {
  offline: false,
  latency: 400, // ms
  downloadThroughput: (500 * 1024) / 8, // bytes/s
  uploadThroughput: (250 * 1024) / 8,
});

It works in Chromium only. Offline is emulated with await context.setOffline(true) (and false to "bring back" the network): this checks a "no connection" banner, a queue of deferred actions and recovery. Individual requests are slowed in page.route by adding a delay before route.continue(). For a more realistic check at the OS level, use system emulators (Network Link Conditioner, tc / toxiproxy).

Intercepting and mocking requests, proxy tools

Interception inside the test itself. page.route(url, handler) (or context.route, which covers all pages of a context) allows three actions: route.fulfill() to return your own response, route.continue() to let it through (changing headers or the body if needed), route.abort(code) to break it with a network error. Error codes for abort: 'connectionrefused', 'connectionreset', 'timedout', 'namenotresolved', 'internetdisconnected', 'failed' and others.

// the server is unreachable: check the error message
await page.route('**/api/orders', (route) => route.abort('connectionrefused'));

// slowing down one request
await page.route('**/api/report', async (route) => {
  await new Promise((r) => setTimeout(r, 5000));
  await route.continue();
});

abort models real network failures (fetch ends with a TypeError, Network shows failed), while fulfill({ status: 503 }) is a server response with an error code. A UI usually handles these two cases differently, so test both. Mocking always covers the real integration, so mocks complement a few e2e tests without mocks rather than replace them.

Proxy tools sit between a client and a server and work with any client (a mobile app, a desktop app), not only the Playwright browser:

  • Charles, Fiddler, Proxyman: a GUI, viewing and changing traffic, Map Local/Map Remote, throttling, breakpoints;
  • mitmproxy: the same approach but with Python scripts and a command-line mode, good for automation;
  • for HTTPS their root certificate has to be added to the trusted ones on the device (on Android, from a certain version, apps do not trust user roots without a manifest permission, and certificate pinning blocks such traffic completely).

Typical scenarios: replace a backend response without changing code, reproduce a 500 or a slow response on mobile, see what exactly an SDK sends, check that the app does not send extra data. For a pinned client, use a test build without pinning.

Diagnosing network failures: curl, dig and error classes

An unstable test with a "network" error must be narrowed down to a layer. The order: name → connection → TLS → HTTP → application.

SymptomLayer and typical cause
ENOTFOUND, ERR_NAME_NOT_RESOLVEDDNS: a typo in the name, no record, a resolver or VPN
ECONNREFUSEDTCP: nobody listens on the port (the service did not start, the wrong port, listening only on 127.0.0.1)
Timeout with no responseTCP/network: a firewall drops packets, the host is unreachable
ECONNRESET, socket hang upThe connection was aborted by the other side: a process crash, an idle timeout, overload
ERR_CERT_*, UNABLE_TO_VERIFY_LEAF_SIGNATURETLS: expired, self-signed or incomplete chain, name mismatch
502 Bad GatewayThe proxy got an invalid response or could not connect to the upstream (the upstream is down, restarting)
503 Service UnavailableThe service is unavailable or overloaded (no healthy backends)
504 Gateway TimeoutThe proxy did not get an upstream response within its timeout

Important: 502/503/504 come from a proxy or balancer, so the connection to it works and the problem is behind it. The response body (nginx/CDN HTML instead of JSON) and the Server/Via header hint at who returned it.

Tools.

curl -v https://example.com/health               # DNS, TLS, request and response headers
curl -sS -o /dev/null -w "dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com
curl --resolve example.com:443:203.0.113.10 https://example.com   # bypass DNS, specify the IP
dig +short example.com A                         # what DNS says
dig @1.1.1.1 example.com                         # another resolver
ping example.com                                 # host reachability (ICMP may be disabled)
traceroute example.com                           # tracert on Windows; the path and where packets vanish
nc -vz example.com 443                           # is the port open (on Windows: Test-NetConnection)

Approach to a flaky network. Find a pattern: time of day, a particular runner or region, the first request after idle (a cold connection, DNS), exactly N seconds (a timeout). Add the URL, time, status, x-request-id and a trace/HAR to the test logs; do not cure it with a retry without understanding the cause. For API calls from a CI test runner, keep-alive, a connection pool and explicit client timeouts are worth having.