In a Cypress `cy.intercept()` reply, how do `delay` and `throttleKbps` differ?
answer
- One waits, the other trickles
- Milliseconds versus a transfer rate
- Does the header arrive on time?
- Small stub bodies barely feel a rate cap
- Response-handler forms set the same fields
basics
~20 sdelay holds the whole response back for a number of milliseconds and then sends it at full speed. throttleKbps leaves the wait unchanged but streams the body slowly. One models latency, the other models bandwidth.
solid answer
~40 sBoth are optional properties of a Cypress StaticResponse, and they shape different halves of a slow response. `delay` is a number of milliseconds Cypress waits **before the response is sent at all** — status line, headers and body are all held back, then delivered normally. `throttleKbps` caps the rate at which the body is streamed, so the response starts on time but takes longer to finish. They compose: `{ fixture: 'stations.json', delay: 800, throttleKbps: 60 }` is a slow-to-answer, slow-to-transfer station list. In a response handler the same two knobs are `res.setDelay(ms)` and `res.setThrottle(kbps)`, which set the same properties on a real response and return the response so they can be chained.
code
javascript · 17 linesit('shapes two routes differently', () => {
// latency: answers 1.5 s late, then arrives at full speed
cy.intercept('GET', '/api/stations*', {
fixture: 'stations.json',
delay: 1500,
}).as('getStations')
// bandwidth: answers at once, then streams the archive slowly
cy.intercept('GET', '/api/stations/archive*', {
fixture: 'station-archive.json',
throttleKbps: 60,
}).as('getArchive')
cy.visit('/dashboard')
cy.wait('@getStations').its('response.statusCode').should('eq', 200)
cy.wait('@getArchive').its('response.statusCode').should('eq', 200)
})go deeper
Learn the two property names and their units: delay in milliseconds, throttleKbps as a transfer rate, both optional fields on the object you hand to cy.intercept().
Explain what is held back by each — the whole response versus the body stream — and why a rate cap is nearly invisible on a two-kilobyte stub.
Talk about the interaction with requestTimeout and responseTimeout, and about picking the smallest injected slowness that makes a behaviour observable instead of a dramatic one.
Decide how much simulated slowness belongs in a suite at all, given that every injected millisecond is wall-clock time paid on every run in the pipeline.
`delay` and `throttleKbps` are the two properties on a Cypress **StaticResponse** that make a route slow instead of failed. They are often used interchangeably, and they should not be: on a small JSON payload one of them is clearly visible and the other does almost nothing. ## `delay` — latency before anything arrives `delay` is a number of **milliseconds Cypress waits before the response is sent**. Nothing leaves the interceptor during that window: no status line, no headers, no first byte. When the timer fires, the response is delivered at full speed. ```js cy.intercept('GET', '/api/forecast*', { fixture: 'forecast.json', delay: 1200, }).as('getForecast') ``` That models a service that takes 1.2 seconds to think. Cypress validates the value when the route is declared: it must be a finite, non-negative number, and it must be under 2,147,483,647 ms — beyond that the underlying timer would silently treat it as ~1 ms, so Cypress rejects it rather than lying to you. ## `throttleKbps` — how fast the body streams `throttleKbps` caps the **transfer rate of the response body**. The response starts on time; it just dribbles out. Internally Cypress pipes the body through a throttling stream constructed from `throttleKbps * 1024` bytes per second, and it is constructed only after any `delay` has elapsed, so the two are additive rather than overlapping. Two consequences follow, and they are the reason people are disappointed by throttling: - **A small body cannot be throttled into visibility.** A 2 KB forecast JSON at `throttleKbps: 50` finishes in roughly forty milliseconds. If you want the dashboard to sit in its pending state, `delay` is the knob, not throttle. - **Only the body is rate-limited.** Status and headers are already on the wire, so a request that has "started" from the application's point of view can still be throttled for a long time afterwards. Be careful about the unit. Cypress's own type definition describes `throttleKbps` as **kilobytes** per second and the implementation multiplies by 1024 bytes, while the option table in the reference documentation says kilobits. Treat the number as a shaping knob for tests, not as a calibrated bandwidth measurement. ## Side by side | | `delay` | `throttleKbps` | |---|---|---| | Unit | milliseconds | rate applied as `value * 1024` bytes/second | | What waits | the entire response, including headers | only the body stream | | Visible on a 2 KB stub | yes, immediately | barely | | Visible on a large fixture | yes | yes | | Models | server think time, round-trip latency | a constrained connection | | Response-handler form | `res.setDelay(ms)` | `res.setThrottle(kbps)` | ## Shaping a real response, not a stub Both properties also exist on the response object in a response handler, where the reply is the **real** server's. `res.setDelay()` and `res.setThrottle()` set the same two fields and return the response, so they chain: ```js cy.intercept('GET', '/api/stations*', (req) => { req.on('response', (res) => { res.setDelay(500).setThrottle(120) }) }) ``` This is the only way to slow a response you are not replacing — the station list still comes from the server, it just arrives late and slowly. ## Where the wait is actually spent Both properties are applied on Cypress's side of the exchange, not in the browser. The application makes its request; Cypress holds the reply for `delay` milliseconds, then streams the body under whatever rate cap is set, and only then does the browser's promise settle. Two things follow: - `cy.wait('@getForecast')` does not resolve until the response is complete, so injected slowness is spent **inside** the wait rather than after it. - A fixture-backed reply is delayed exactly like an inline `body`; reading the file happens before the timer, not after it. The two knobs are additive, and in that order. A `{ delay: 800, throttleKbps: 60 }` reply on a 30 KB station archive starts arriving after 800 ms and then takes roughly half a second more to finish, where the same options on a 2 KB forecast are, in practice, just the 800 ms. ## The ceiling you are working against Injected slowness is spent out of Cypress's own patience budget. `cy.wait('@getForecast')` allows `requestTimeout` (5000 ms by default) for the request to be made and `responseTimeout` (30000 ms by default) for the response to arrive, so a `delay` near or beyond 30 seconds fails the wait with a timeout rather than exercising the dashboard's slow path. Any command asserting on the resulting DOM is separately bounded by `defaultCommandTimeout`. In practice: 1. Choose the smallest delay that makes the behaviour observable — usually hundreds of milliseconds, not seconds. 2. Reserve throttling for genuinely large payloads, such as a bundled station archive. 3. If a scenario needs a delay near the timeout ceiling, raise the timeout on that one command deliberately, and comment why.
- Where do `res.setDelay()` and `res.setThrottle()` fit compared with the StaticResponse properties?They are convenience methods on the response object inside a response handler, and they set the same `delay` and `throttleKbps` fields. Use them when the response is the real one and you only want to shape it; use the StaticResponse properties when the test is replacing the response entirely. Both return the response, so calls chain.
- Why might `throttleKbps` appear to do nothing on a stubbed route?Because it limits the rate of the body only, and stub bodies are usually tiny — a couple of kilobytes finishes in a few tens of milliseconds no matter how low the cap. Throttling shows up on large fixtures; for a visible pending state on a small payload, use `delay`.
saying these in an interview costs you the question
- Thinks delay pauses only the body and sends headers early
- Uses throttleKbps to hold a small JSON stub pending
- Believes delay counts against defaultCommandTimeout only
- Says throttleKbps is measured in milliseconds
- Assumes res.setDelay works on a stubbed StaticResponse route