Proving the UI Sent the Right Request: Reading the Network Panel
The form accepts a discount code, the UI flashes "Applied", and the order total still ignores it. The screen told you what the developer hoped you would believe. Open DevTools, select the Network panel, tick Preserve log, and repeat the action — now you can see the contract between the browser and the server instead of the marketing copy on top of it.
Every panel row is one HTTP exchange. Sort by Name, filter by Fetch/XHR to drop images, and read the four fields that decide whether the client did its job:
| Field | What to verify | Typical defect it exposes |
|---|---|---|
| Request URL + method | Correct endpoint, POST for a write, query params present |
Code applied to /cart, never re-sent to /order |
| Payload (Headers → request) | Body matches what the UI displayed — right key, right type | discountCode: 10 (number) when the API expects "SAVE10" |
| Status | 2xx for success; expect 4xx on bad input, never 200-with-error | 200 OK carrying {"error":"expired"} |
| Response body | Server-side result, IDs, recalculated totals | Total unchanged because the server ignored the field |
Click a row to open it. Headers shows the method and status; Payload the outgoing body; Preview/Response the server's reply. Copy the row's cURL with right click → Copy as cURL and replay it in a terminal — that one move separates "the browser broke it" from "the API broke it", and it is the fastest way to write an API-level defect report from a UI observation.
Three network checks belong in every manual pass. Idempotency and double submission: click Pay twice and watch for two POST /orders rows — a duplicate order is a defect even when the UI looks fine. Status handling: find the requests returning 4xx or 5xx (use the filter box, type 404) and confirm the UI tells the user something actionable rather than silently swallowing the failure. Writes that never happened: an action that appears to succeed with no network call at all means the handler threw before dispatch — check the Console panel for the exception, which is covered below.
Enable Preserve log before navigating so completed requests survive page changes, and use Clear at the start of each journey so your notes map to one run. Time to first byte and the size column also give you free data: a 1.2 MB JSON response for a list of 10 rows is a performance defect you can file with numbers instead of adjectives.
Two more readings earn a place in every pass. The Timing tab decomposes each request into queueing, DNS, TLS, waiting (TTFB), and download: a 900 ms waiting phase against a local API means slow server work, not a slow browser, and that distinction decides who the defect is assigned to. Response headers tell you what the browser may do with the answer — a missing content-type: application/json on an endpoint that returns JSON is a defect that surfaces later as a parse failure, and a missing cache-control on a static asset explains why a stale image outlived your release. Filter by type (JS/CSS/Img) to hunt weight: a 780 KB bundle for a login page is a first-paint defect you can quantify with no performance tooling at all.
Correlation is why the panel beats guessing. The Initiator column names the line of JavaScript that caused each request — if a POST /cart appears while you merely scrolled, an analytics hook is bound to scroll events, and you have symptom and source in one screenshot. Right click → Save all as HAR and attach it: a reviewer opens your file and sees every request, header, and response you saw without reproducing anything.
The Network panel answers "what did the client send". The next section answers "what happens when the path between client and server is terrible" — the condition your users are actually on.
Making a Fast Laptop Behave Like a Slow Phone: Network and CPU Throttling
Everything works on your office connection and a 3.5 GHz laptop. Your user is on a train with two bars of signal holding a three-year-old phone, and that is where your feature actually lives. DevTools can reproduce both halves of that condition without leaving your desk.
Network throttling. Open the Network panel's throttling dropdown (top toolbar, next to the filter) and pick Slow 3G, or open Network conditions for custom profiles. The effect is immediate and measurable: requests are delayed and rate-limited, so you get to watch what your UI does during the wait. What to check while throttled:
- Does a loading state appear at all, or does the screen sit frozen and dead?
- Does the button disable on submit, and re-enable if the request fails?
- If the user clicks again during the wait, do you get duplicate POSTs?
- Does the layout jump when content finally arrives (cumulative shift)?
- Do timeouts produce a retry prompt or a permanent spinner?
The Offline checkbox in the same dropdown is the cheapest high-yield test there is: flip it, press Save, and observe whether the app reports the failure, queues the action, or pretends success. Undo it and confirm the UI recovers without a manual page reload.
CPU throttling. Rendering cost is separate from network cost. In the Performance panel's toolbar (or More tools → Rendering for a persistent setting), apply 4x–6x CPU throttling and repeat the journey. JavaScript that took 40 ms now takes 240 ms — long enough to cross the 200 ms threshold at which a tap stops feeling responsive. Watch for: input that lags behind typing, scroll that stutters, list rendering that freezes the main thread, and animations that drop frames. A search box that feels instant on your machine but drops every third keystroke at 6x is a real defect with a reproducing condition you can state in one line.
Record the exact profile in your notes — Slow 3G + 6x CPU, journey 'guest checkout', input lag at step 4 — because a defect without its throttle profile is unrepeatable, and "feels slow" is the fastest way to get a bug marked cannot reproduce. Quantify instead: under Slow 3G the product page takes 4.8 s to become interactive versus 0.9 s unthrottled; that number goes straight into the report.
Two numbers make throttled findings defensible instead of anecdotal. First, time to interactive: record with the Performance panel and mark when the search box would actually accept typing, not when pixels first appeared — a page that paints in 1.2 s but responds at 4.9 s is slow, and only the second number matches the user's complaint. Second, the waterfall's arithmetic: Slow 3G caps throughput near 400 kbps, so a 1.8 MB page has a floor around 36 seconds. Seeing that number lets you tell the team this cannot be cured by better caching of one small asset — the page is simply too heavy — which is a sizing fact, not an opinion.
One condition DevTools will not simulate for you is an unstable network rather than a slow one. The closest approximation is the Blocking tab under Network conditions: block your API host, provoke the action, and watch the UI's failure handling; then unblock and confirm it recovers without a manual reload. A checkout that shows "Something went wrong" with a retry button passes. One that spins forever fails, and you found it without leaving the browser.
Throttling tells you the feature survives bad conditions. It does not tell you whether the code threw warnings nobody is looking at — the daily habit for that is the Console, together with viewport simulation, in the next section.
Console Errors Every Session, and Device Mode Before the Device Pass
Two DevTools habits cost almost nothing and continuously find defects that the UI hides: reading the Console at every step, and switching the viewport before you ever pick up a physical phone.
The Console is part of the walkthrough, not a post-mortem tool. Clear it at the start of a journey (console.clear() or the ⊗ button), then look after each significant step. Treat these as findings, not noise:
Uncaught TypeError: cannot read properties of null (reading 'total') → feature-breaking JS error
404 /api/coupons/SAVE10 → broken request, often filtered out of the Network view
Failed to load resource: net::ERR_CERT_DATE_INVALID → expired cert on a third-party script
React key warning / DOM removed while listeners attached → leak or duplicated rows after re-render
Filter with Errors and Warnings levels, and remember the rule of thumb: zero uncaught errors during a normal journey. An exception in the Console that the user cannot see is still a defect — it means code after that line never ran, which shows up three interactions later as a button that does nothing. Pair it with the Network panel's Preserve log so you can tie each error to the request that triggered it.
Failed requests deserve their own filter. Type 404 or 500 into the Network filter box and count. A clean-looking page firing four 404s for analytics or a font is a defect worth filing: it slows first paint, breaks offline behavior, and usually indicates a deploy that left stale asset paths behind.
Device mode is toggled with Ctrl+Shift+M (or the phone/tablet icon). It does two things: it constrains the viewport to a device's dimensions and density, and it can override the user agent and emulate touch. Use it to sweep breakpoints fast:
360×740 common Android — check the narrowest realistic layout
390×844 iPhone 14 — check notch/home-indicator safe areas
768×1024 tablet portrait — check the tablet-specific breakpoint
1280×800 desktop baseline before you claim nothing regressed
At each width verify: no horizontal scroll bar, no overlapping or clipped controls (text truncation of a price is a defect), tap targets at least ~44 px, and menus that collapse to something reachable with a thumb. Turn touch emulation on to test tap behavior and Device Pixel Ratio to catch blurry canvas or border rendering.
Be precise about what device mode proves: it shows layout and responsiveness, not real GPU behavior, not iOS Safari's address-bar resize dance, and not actual touch latency. Two layout defects are visible only at narrow widths and worth looking for while you sweep. Overflow sources: a 600 px table inside a 360 px viewport — does it scroll inside its own container with a shadow hint, or drag the whole page sideways (a defect)? Fixed elements: a bottom-fixed cookie banner at 360 px can cover the Add-to-cart button entirely, and at 390×844 the notch can clip a back arrow. Resize the viewport while watching the header; a bar that hides content as it reflows will never show itself at 1280 px.
Console filters are worth mastering too: type 404 in the Filter box to isolate failed loads, prefix a term with - to exclude noise (one third-party chat widget can bury your app's warnings), and keep Group similar on so one repeated message does not flood the pane. Use the ⊗ to clear between steps rather than at the end — a clean-then-act-then-read rhythm is what makes an error attributable to a specific step.
Device mode is the cheap filter that decides which physical devices justify a full pass — the subject of Cross-Browser and Device Coverage. Use it as the working companion to Manual Web Testing Workflow, then take to real hardware everything it cannot emulate.
Quiz
1. A discount code shows 'Applied' in the UI but the order total never changes. What is the most informative first DevTools move?
2. You click Save twice on a slow connection and two identical POST /orders requests appear. What does this demonstrate?
3. Which combination best reproduces a low-end phone experience on a fast laptop?
4. What does emulating a mobile viewport in device mode reliably NOT prove?
Flashcards
Question
Which four Network-panel fields verify that a UI action produced a correct request?
Click to reveal answer
Answer
URL and method (right endpoint, POST for writes, query params present), payload (body matches what the UI displayed — key names and types), status (2xx success, 4xx for bad input, never 200-wrapped errors), and response body (recalculated totals, created IDs, actual server outcome).
Question
What does 'Copy as cURL' from the Network panel give you, and why is it valuable in triage?
Click to reveal answer
Answer
A shell command that replays the exact request — same headers, payload, cookies — outside the browser. Pasting it into a terminal separates 'the browser broke it' from 'the API broke it' in one step and converts a UI observation into an API-level defect report anyone can reproduce.
Question
Name five things to check while a journey runs under Slow 3G throttling.
Click to reveal answer
Answer
A loading state appears instead of a frozen screen; the submit button disables and re-enables on failure; repeat clicks do not create duplicate POSTs; layout does not shift when content arrives; timeouts produce a retry prompt rather than a permanent spinner. Add the Offline checkbox: does the app report, queue, or falsely claim success?
Question
What is the working rule for Console errors during a manual journey, and why does a hidden exception still count as a defect?
Click to reveal answer
Answer
Clear the Console at journey start and treat any uncaught error as a finding — zero is the pass condition. An exception the user cannot see means every statement after it never executed, so functionality downstream dies silently: the button three interactions later simply does nothing, which surfaces far from where the error appeared.
Question
What does device mode emulate, and what does it fail to reproduce?
Click to reveal answer
Answer
It constrains the viewport to device dimensions/density and can override user agent, pixel ratio, and touch — enough to verify breakpoints, horizontal scrolling, clipping, and tap-target size. It does not reproduce the real GPU, the actual browser engine (especially iOS Safari's viewport resize behavior), or real touch latency, so it filters devices rather than replacing a hardware pass.
Revision Notes
Key Takeaways
- 1. Trust the Network panel over the UI: payloads, status, and response bodies are the contract; 'Applied' on screen proves nothing
- 2. Copy-as-cURL replay is the one-move test that separates browser bugs from API bugs
- 3. Slow the network and the CPU independently — most 'slow on mobile' defects need both reproductions to appear
- 4. Zero uncaught Console errors is a pass condition; a hidden exception means later code never ran
- 5. Device mode finds layout defects cheaply but cannot emulate the real engine or touch stack, so it filters devices rather than replacing them
Interview Tips
- • "How do you verify an API call from the UI?" — Network filter Fetch/XHR, then URL/method, payload, status, response; add Copy-as-cURL replay to show you can localize the fault to client or server
- • "How do you test for slow networks?" — throttle to Slow 3G, name the specific checks (loading state, button lock, duplicate POSTs, layout shift, retry on timeout), and mention Offline mode for failure-path behavior
- • "You can only test on one device. What do you do?" — device mode sweeps the breakpoints first, then pick real hardware from analytics-driven coverage; stating the priority logic beats 'test on iPhone'
- • "A user says the page is slow but it's fast for you. What's your process?" — apply network + CPU throttling, quantify (interactive time, input latency), record the profile, then file with numbers instead of adjectives
Cheat Sheet
Testing with Browser DevTools
Network audit for one action:
| Field | Verify | Defect signal |
|---|---|---|
| URL + method | endpoint, POST for writes | code sent to the wrong path |
| Payload | body matches displayed values | "10" sent as number, wrong key |
| Status | 2xx / 4xx as appropriate | 200 carrying {"error":...} |
| Response | totals, IDs, server result | server ignored the field |
Preserve log: ON before navigating · Clear at journey start
Filter: Fetch/XHR · type 404 or 500 to count failed requests
Right-click → Copy as cURL → replay in terminal (client vs API)
Throttling matrix:
| Setting | Reproduces | Look for |
|---|---|---|
| Slow 3G | constrained network | loading state, button lock, duplicate POSTs, shift |
| Offline checkbox | no connectivity | report / queue / false success + recovery |
| 4x–6x CPU (Performance panel) | weak device | input lag >200 ms, frozen list render, dropped frames |
Record the profile: Slow 3G + 6x CPU, guest checkout, lag at step 4 — unthrottled 'feels slow' gets marked cannot-reproduce. Quantify: 4.8s interactive vs 0.9s.
Console habit: clear at start, check after each step, filter Errors/Warnings. Uncaught TypeError = downstream code never ran. Zero uncaught errors is the pass condition.
Device-mode sweep: 360×740 · 390×844 · 768×1024 · 1280×800 → no horizontal scroll, no clipped price text, ~44px targets, reachable menu. Emulation ≠ real GPU/Safari/touch — it only picks which hardware gets a full pass.