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.
| Symptom | Layer and typical cause |
ENOTFOUND, ERR_NAME_NOT_RESOLVED | DNS: a typo in the name, no record, a resolver or VPN |
ECONNREFUSED | TCP: nobody listens on the port (the service did not start, the wrong port, listening only on 127.0.0.1) |
| Timeout with no response | TCP/network: a firewall drops packets, the host is unreachable |
ECONNRESET, socket hang up | The connection was aborted by the other side: a process crash, an idle timeout, overload |
ERR_CERT_*, UNABLE_TO_VERIFY_LEAF_SIGNATURE | TLS: expired, self-signed or incomplete chain, name mismatch |
| 502 Bad Gateway | The proxy got an invalid response or could not connect to the upstream (the upstream is down, restarting) |
| 503 Service Unavailable | The service is unavailable or overloaded (no healthy backends) |
| 504 Gateway Timeout | The 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.