In Cypress, why is cy.fixture() a poor way to load a 40 MB receipt archive?
answer
- Nothing about this is streamed
- Two processes end up holding it
- The default encoding makes it bigger
- One socket message carries the payload
- A path argument avoids the round trip
basics
~20 sBecause it reads the whole file into memory in the Cypress Node process, ships it to the browser as a single internal WebSocket message, and keeps it cached. Large archives can exhaust memory or exceed socket buffer limits.
solid answer
~40 s`cy.fixture()` is built for small canned inputs. It reads the **entire** file into memory in the Cypress Node process and transfers the contents to the browser as one message over the internal WebSocket, then holds the result in the fixture cache for the rest of the run. There is no hard size limit, but a 40 MB zip means the bytes exist twice — once in Node and once in the browser — plus a base64 expansion if you did not pass `null` as the encoding, and payloads approaching 100 MB can exceed socket-level buffer limits. The practical ceiling is a few megabytes. For an upload, hand `.selectFile()` the path so the bytes never enter your test; for metadata or a server-side operation, keep the work in Node behind `cy.task()`.
go deeper
Be ready to say that fixtures are meant to be small canned inputs and that a big file belongs in a file input via a path, not loaded into the test first.
Explain the round trip: read fully into memory in Node, one message over the internal socket, base64 expansion by default, and a cached copy held for the rest of the run.
Show that you can recognise the failure by shape — a run that hangs or dies without a useful message — and that you would move bulk work to the process that already holds the file.
Own the limit as a standard: what size fixtures a suite is allowed to carry, whether large binaries belong in the repository at all, and how a subset is kept representative enough to be worth testing against.
## What the command actually does with the bytes `cy.fixture()` looks like a file read, but it is a round trip. The Cypress Node process opens the file, reads it **completely** into memory, encodes it, and sends the contents to the browser as a single message over the internal WebSocket that connects the two halves of Cypress. The browser-side driver then keeps the result in its fixture cache so later calls need no second trip. For a 6 KB `receipts.json` none of that matters. For a 40 MB archive of receipt scans, every step in that chain becomes a cost: - The whole file must fit in the Node process's memory at once — there is no streaming. - A copy then has to fit in the browser's memory as well. - Unless you pass `null` as the encoding, binary content is **base64-encoded**, which inflates it by roughly a third before it crosses the socket. - The cache holds the result for the rest of the run, so the peak is not transient. - The transfer is one socket message; payloads that approach or exceed 100 MB can exceed socket-level buffer limits. The symptom is rarely a clean error. Runs go quiet, the browser tab becomes unresponsive, or the run dies with a crash that names nothing recognisable — which is what makes this worth recognising by shape rather than by message. ## A practical ceiling Treat a few megabytes as the working limit for anything loaded through `cy.fixture()`, and prefer a representative subset over a faithful copy. In an expense-report suite that usually means a fixture holding five receipts rather than a quarter's worth, and one small sample scan rather than a folder of them. When a fixture starts growing, three questions usually shrink it again: 1. **What does the assertion actually read?** A test that checks a reimbursement total needs the amounts, not the scanned image attached to every receipt. 2. **Does the size itself matter to the behaviour under test?** If the point is that an oversized upload is rejected, the interesting artefact is the rejection, and the file can often be produced rather than committed. 3. **Does this belong in the repository at all?** A large binary under `cypress/fixtures` is cloned by every developer and every CI job forever, whether or not the spec that needs it runs. ## If the file is going into a file input Loading the bytes just to hand them to an `<input type="file">` is the most common reason people reach for a large fixture, and it is the case with the cleanest answer: give `.selectFile()` the path and let Cypress do the work without the contents passing through your test. ```js cy.get('[data-testid="receipt-upload"]') .selectFile('cypress/fixtures/scans/quarterly-receipts.zip') ``` The fixture-loading round trip is skipped entirely. The same command also accepts a fixture alias or in-memory contents when you do want to shape the payload yourself — but for a big file, the path form is the one that scales. ## If you only need a fact about the file Sometimes the test does not need the bytes at all: it needs to know that an export exists, how large it is, or that a checksum matches. Reading the file into the browser to answer that is pure waste. Keep the work in the Node process behind `cy.task()`, and return only the small answer — a size, a boolean, a hash — to the test. The registered handler runs in Node, where the file already lives. ## Choosing the right door | Need | Command | Why | |---|---|---| | Small canned JSON or text input | `cy.fixture()` | parsed for you and cached after one read | | A file that changes during the run | `cy.readFile()` | re-reads and retries, no cache | | Bytes for a file input | `.selectFile()` with a path | contents never enter the test | | A fact about a large file | `cy.task()` | the work stays in Node, only the answer crosses | ## The judgement behind it The real question an interviewer is asking here is whether you notice the cost of a convenience command. `cy.fixture()` hides a Node-to-browser transfer behind a one-line call, and hidden costs only surface under load — on the slowest CI machine, in the longest spec, at the end of a run. A suite that keeps fixtures small and pushes bulk work to the process that already has the file avoids the entire class of failure, and it does so without anyone needing to know the socket limit.
- How do you attach a large receipt archive to a file input in Cypress without loading it?Pass the path to `.selectFile()`: `cy.get('[data-testid="receipt-upload"]').selectFile('cypress/fixtures/scans/quarterly-receipts.zip')`. Cypress attaches the file without the contents travelling through your test code, so nothing is base64-encoded and nothing is held in the fixture cache.
- What does passing null as the encoding change for a large binary fixture?It avoids the base64 expansion. Without it, Cypress base64-encodes binary fixtures, adding roughly a third to the payload that crosses the internal socket. With `null` you receive a `Cypress.Buffer` of the raw bytes instead — smaller, though the file still has to fit in memory twice.
saying these in an interview costs you the question
- Assumes cy.fixture() streams the file to the browser
- Thinks fixture size is bounded by a documented limit
- Loads a large archive just to fill a file input
- Ignores base64 expansion on binary fixtures
- Expects a clear error rather than a hung run