Resource library

QA How-To

How to Use Cypress cy.fixture (2026)

Learn cypress cy.fixture with JSON, aliases, TypeScript, API stubs, encodings, caching, uploads, test data design, and debugging patterns for reliable 2026 CI.

20 min read | 2,649 words

TL;DR

cypress cy.fixture loads a fixed file from the fixtures folder and yields parsed or encoded contents. Use it for small deterministic inputs, consume data with the Cypress chain, and avoid mutating cached baselines.

Key Takeaways

  • Load small static files relative to the configured fixtures folder and consume them through the Cypress command chain.
  • Use the intercept fixture shortcut for exact API responses, or load and clone data when a scenario needs a variation.
  • Treat fixture objects as immutable baselines because Cypress caches fixture contents and JavaScript objects are mutable.
  • Use TypeScript generics for editor support, but add separate runtime validation when a fixture contract is critical.
  • Choose cy.readFile for changing files, selectFile for uploads, and cy.task for large Node-side operations.
  • Keep fixtures synthetic, focused, reviewed, and free of production records, credentials, and private data.

cypress cy.fixture loads a fixed data file from the configured fixtures folder and yields its parsed or encoded contents to your test. Use it for small, stable examples such as users, products, API responses, CSV samples, and images, not as a replacement for database setup or runtime data generation.

The command looks simple, but reliable fixture design depends on timing, aliases, mutation, TypeScript types, file encoding, and Cypress's fixture cache. This guide explains those details and shows maintainable patterns that work with current Cypress APIs in 2026.

TL;DR

{
  "id": 1042,
  "name": "Avery Chen",
  "role": "qa-engineer"
}
describe('profile', () => {
  it('renders fixture-backed user data', () => {
    cy.fixture('users/avery.json').then((user) => {
      cy.intercept('GET', '/api/users/1042', { body: user }).as('getUser')
    })

    cy.visit('/users/1042')
    cy.wait('@getUser')
    cy.contains('h1', 'Avery Chen').should('be.visible')
  })
})
Need Best Cypress tool
Load a small static file once cy.fixture()
Re-read a file that changes during the test cy.readFile()
Upload an existing file .selectFile('cypress/fixtures/file.pdf')
Perform large file work in Node cy.task()
Return a fixture from an intercepted API cy.intercept(..., { fixture: 'file.json' })

1. What cypress cy.fixture Does

cy.fixture(filePath) reads a file relative to fixturesFolder, which defaults to cypress/fixtures. For recognized formats, Cypress chooses an encoding and transforms the content. A JSON fixture yields an object, a text fixture yields a string, and a binary fixture can be requested as a Cypress.Buffer by passing null as the encoding.

The command is queued like other Cypress commands. It does not return file data synchronously, so this is incorrect:

const user = cy.fixture('user.json')
// user is a Chainable, not the parsed user object

Consume the yielded subject with .then(), an alias, or another Cypress command:

cy.fixture('user.json').then((user) => {
  expect(user.name).to.eq('Avery Chen')
})

Fixtures are loaded once and cached for the test run. If a test overwrites the underlying file, a later cy.fixture() call can still yield the originally loaded content. That behavior is intentional because fixtures represent fixed test inputs. Use cy.readFile() when the file itself is the changing subject.

cy.fixture() must be chained from cy. It does not create retrying assertions around the file content. An assertion added directly after it runs once because a static file does not become correct through browser retries. Validate fixture syntax in source control and keep the data deterministic.

The command also does not log an entry in the Cypress Command Log. When debugging, add a focused assertion or cy.log() with non-sensitive summary information rather than printing an entire confidential payload.

2. Set Up cypress cy.fixture Files and Paths

Place fixture files under cypress/fixtures in a standard Cypress project. Organize them by feature when a suite has more than a few files. A predictable directory is easier to review than a flat folder of vaguely named JSON documents.

cypress/
  fixtures/
    accounts/
      active-user.json
      suspended-user.json
    catalog/
      products.json
    files/
      valid-avatar.png
  e2e/
    account-profile.cy.ts

Reference nested files from the fixture root:

cy.fixture('accounts/active-user.json').then((account) => {
  expect(account.status).to.eq('active')
})

You may omit a recognized extension, but explicit extensions reduce ambiguity. When the extension is absent, Cypress searches supported file types in a defined order and uses the first match. A later contributor could add active-user.js beside active-user.json and make intent less obvious. cy.fixture('accounts/active-user.json') communicates exactly which contract is loaded.

You can change the folder in cypress.config.ts:

import { defineConfig } from 'cypress'

export default defineConfig({
  fixturesFolder: 'test-data/fixtures',
  e2e: {
    baseUrl: 'http://localhost:3000',
  },
})

Keep that configuration exceptional. Conventional locations help new team members, IDE searches, and CI troubleshooting. Do not point fixturesFolder at production exports or a shared mutable directory. Fixtures should be versioned, reviewed, synthetic, and safe to expose in test artifacts.

For broader project setup, pair this guide with the Cypress testing tutorial and keep fixture organization aligned with spec ownership.

3. Load JSON, Text, CSV, and Binary Fixtures

Cypress automatically handles common extensions including JSON, JavaScript, HTML, text, CSV, PNG, JPEG, GIF, TIFF, and ZIP. The yielded type depends on the extension and encoding. JSON is parsed into JavaScript data, while CSV remains text unless your test parses it.

cy.fixture('catalog/products.json').then((products) => {
  expect(products).to.be.an('array').and.have.length.greaterThan(0)
})

cy.fixture('messages/welcome.txt').then((message) => {
  expect(message.trim()).to.eq('Welcome back')
})

cy.fixture('imports/users.csv', 'utf8').then((csv) => {
  expect(csv).to.include('email,status')
})

Supported explicit encodings include ascii, base64, binary, hex, latin1, utf8, utf-8, ucs2, ucs-2, utf16le, and utf-16le. Pass null when you need a buffer regardless of the extension:

cy.fixture('files/valid-avatar.png', null).then((image) => {
  expect(Cypress.Buffer.isBuffer(image)).to.eq(true)
  expect(image.length).to.be.greaterThan(0)
})

Choose the lowest-level representation only when the assertion requires it. If the goal is to upload the image through a file input, .selectFile('cypress/fixtures/files/valid-avatar.png') is clearer and avoids first moving the full fixture into test code.

Do not use a fixture as a parser testing shortcut. If CSV import behavior matters, upload the CSV and assert what the application displays or sends. Inspecting the raw CSV in the test only confirms that the repository file contains a header.

4. Work with Command Queues, then(), and Aliases

Cypress builds a command queue and runs it after the test callback schedules commands. Ordinary variables are evaluated by JavaScript immediately, which is why assigning inside .then() and reading outside the chain often causes confusion.

let accountName: string

cy.fixture('accounts/active-user.json').then((account) => {
  accountName = account.name
})

// This executes after the fixture command because it is queued.
cy.then(() => {
  expect(accountName).to.eq('Avery Chen')
})

Keeping dependent work inside the chain is usually simpler:

cy.fixture('accounts/active-user.json').then((account) => {
  cy.visit(`/accounts/${account.id}`)
  cy.contains('h1', account.name).should('be.visible')
})

Aliases can share a yielded fixture within a test:

beforeEach(() => {
  cy.fixture('accounts/active-user.json').as('activeAccount')
})

it('shows the account status', function () {
  cy.get<{ name: string; status: string }>('@activeAccount').then((account) => {
    cy.visit('/account')
    cy.contains(account.name).should('be.visible')
    cy.contains('Active').should('be.visible')
  })
})

If you use Mocha's this context, both the hook and test must use function () {} syntax. Arrow functions capture lexical this, so this.activeAccount will not refer to the test context. Many TypeScript teams prefer cy.get('@activeAccount') or closures because the dependency is explicit and typing is easier.

Aliases reset before each test. That isolation is desirable. Load fixtures in beforeEach if every test needs a fresh alias, or inside the individual test when only one scenario uses the data.

5. Stub API Responses with Fixture Data

The most common practical use of cypress cy.fixture is supplying deterministic API data. Register the intercept before the action that sends the request. If the application requests data during startup, define the intercept before cy.visit().

The compact form references a fixture directly:

cy.intercept('GET', '/api/accounts/1042', {
  fixture: 'accounts/active-user.json',
}).as('getAccount')

cy.visit('/account/1042')
cy.wait('@getAccount').its('response.statusCode').should('eq', 200)
cy.contains('h1', 'Avery Chen').should('be.visible')

Load the object first when a scenario must change one field:

cy.fixture('accounts/active-user.json').then((account) => {
  cy.intercept('GET', '/api/accounts/1042', {
    statusCode: 200,
    body: { ...account, status: 'suspended' },
  }).as('getSuspendedAccount')
})

cy.visit('/account/1042')
cy.wait('@getSuspendedAccount')
cy.contains('Account suspended').should('be.visible')

body and fixture are alternative StaticResponse properties. Use one, not both. Add headers, status, delay, or a forced network error only when the scenario needs them.

A stub tests the frontend against a controlled contract. It does not prove that the real backend currently returns that contract. Balance stubbed UI tests with API contract checks and a smaller set of end-to-end paths against integrated services. The Cypress cy.intercept guide covers route matching and request lifecycle in greater depth.

6. Create Typed Fixtures in TypeScript

TypeScript makes fixture assumptions visible, but Cypress cannot infer a domain interface from a JSON filename. Add the type at the boundary where the fixture is consumed.

type AccountFixture = {
  id: number
  name: string
  email: string
  status: 'active' | 'suspended'
  permissions: string[]
}

cy.fixture<AccountFixture>('accounts/active-user.json').then((account) => {
  expect(account.permissions).to.include('reports:read')
  cy.intercept('GET', `/api/accounts/${account.id}`, { body: account })
})

The generic improves editor support and compile-time use, but it does not validate JSON at runtime. A fixture with a missing email can still be parsed. Critical contract suites can validate fixtures against a JSON Schema or a runtime schema during a dedicated check. Avoid adding heavy validation to every UI test if a fast preflight test can cover all fixture files once.

Keep interfaces close to the feature model or export them from a test support module. Do not copy slightly different User types into dozens of specs. Also avoid importing production server models that include fields the browser should never receive. Test types should describe the public contract under test.

For response assertions and schema boundaries, the API testing strategy guide helps separate UI stubs from direct service validation.

7. Design Small, Purposeful Fixture Data

A good fixture contains the minimum realistic data needed to express a scenario. Massive production snapshots are hard to review, carry privacy risk, couple tests to unrelated fields, and consume memory. Synthetic records with obvious intent are safer.

Fixture design Good example Risky example
Business state account-suspended.json user2-final.json
Stable synthetic ID 1042 A live production UUID copied from logs
Focused payload One account and required links A 20 MB environment export
Explicit boundary Empty list fixture One file edited by many tests
Safe values avery@example.test A real customer's email

Prefer separate baseline fixtures for meaningfully different states: active, suspended, empty, unauthorized, and malformed. For a one-field variation, clone with object spread in the test. If many specs apply the same transformation, create a small data builder rather than duplicating mutations.

Fixture filenames should describe behavior, not ticket numbers or authors. Keep dates fixed unless date progression is the scenario. A fixture that becomes expired next month creates hidden time coupling. Use a clock command or builder for relative dates.

Review fixture changes like code. A changed permission, status, or nested relationship can alter dozens of scenarios. Clear ownership and small files make that impact understandable.

8. Mutate Fixture Objects Without Cross-Test Leaks

JavaScript objects are mutable, and a loaded fixture may be cached. Mutating a shared object can make later assertions depend on earlier steps. Treat the loaded value as a baseline and create a new value for each scenario.

cy.fixture<AccountFixture>('accounts/active-user.json').then((baseline) => {
  const suspended: AccountFixture = {
    ...baseline,
    status: 'suspended',
    permissions: [],
  }

  cy.intercept('GET', `/api/accounts/${baseline.id}`, {
    statusCode: 200,
    body: suspended,
  }).as('getAccount')
})

Object spread is shallow. Clone nested objects explicitly when changing them:

const euAccount = {
  ...baseline,
  address: {
    ...baseline.address,
    countryCode: 'DE',
  },
}

structuredClone(baseline) is another option for serializable data in environments that support it, but explicit transformations often communicate intent better. JSON serialization as a clone technique drops unsupported values and obscures the reason for cloning.

Never write scenario changes back to a committed fixture during an ordinary test. Parallel runs can race, local worktrees become dirty, and cached reads do not behave as newcomers expect. If file writing is the feature under test, use a temporary output path and clean it through a controlled Node task.

9. Choose cy.fixture, cy.readFile, selectFile, or cy.task

Several Cypress APIs touch files, but they solve different problems.

API Runs or transfers where Best use Important behavior
cy.fixture() Node read, content transferred to runner Small static test input Loaded once and cached
cy.readFile() Reads a project file through Cypress Assert a file that can change Reads again and supports retrying chains
.selectFile() Acts on a file input or drag target Browser upload workflow Can accept a path, alias, or buffer contents
cy.task() Node event process Database, filesystem, or large-file work Custom task must return serializable data

For a download scenario, use cy.readFile() on the output because the application creates it during the test. For a 50 MB archive upload, pass its path to .selectFile() instead of loading the entire archive with cy.fixture(). For checking archive metadata without transferring bytes to the browser, define a Node-side task.

Avoid turning cy.task() into a general escape hatch. It can bypass the application and introduce state that the UI cannot observe. Use it for setup or operating-system work, then assert the user-facing result.

The selection rule is simple: fixture means fixed input, readFile means current file state, selectFile means user upload behavior, and task means controlled Node-side work.

10. Handle Large and Sensitive Fixtures Safely

cy.fixture() reads the whole file into memory and transfers it from the Cypress Node process to the browser runner. Large binary or JSON fixtures increase memory pressure and internal message size. There is no useful reason to reproduce a production-scale dataset in most UI tests.

Reduce a large payload to a representative subset. If pagination matters, create several small page responses and intercept them by query. If rendering performance matters, generate synthetic data intentionally and run that scenario in a suitable performance harness rather than loading a private export.

Do not commit access tokens, passwords, customer records, private keys, or session cookies in fixtures. A repository is not a secret store. Read runtime secrets from environment configuration only when a test truly needs them, and prefer programmatic authentication that keeps credentials out of screenshots and logs.

Images and documents can contain metadata or embedded personal information. Sanitize them and use a reserved domain such as example.test for synthetic addresses. Add a fixture review checklist to pull requests that introduce binary files.

If a payload must remain large, keep processing in Node with cy.task() and return only the small result required by the assertion. That approach protects runner stability while preserving the intent of the test.

11. Debug Fixture Failures Systematically

Start by classifying the failure: path, syntax, encoding, timing, stale cache, or data contract. A path is relative to fixturesFolder, not the spec file. A fixture named users/admin.json should be loaded with that same relative path.

JSON parse failures usually point to a trailing comma, comment, or unquoted property. JSON is stricter than JavaScript. Run a repository JSON validator in CI so syntax errors fail before browser tests start.

If data is undefined, verify where it is accessed. The command has not resolved when ordinary synchronous code runs. Move the dependent assertion inside .then() or a later cy.then(). If a Mocha context property is missing, replace arrow callbacks with function () {} or avoid this entirely.

If a stub shows old data after cy.writeFile(), remember that fixtures are cached. Use cy.readFile() for a dynamic file, or load once and replace the intercepted response object intentionally.

For binary corruption, set the correct encoding. Use null for a buffer and base64 only when the consumer expects a base64 string. Compare a known signature or size, not a screenshot of unreadable bytes.

Finally, make sure the failure belongs to the fixture. A request that never matched cy.intercept() is usually a route pattern or registration timing problem, not a file read problem. Inspect the Routes instrument and the actual request URL before editing data.

Interview Questions and Answers

Q: What does cy.fixture return?

It yields the contents of a file from the configured fixtures folder. JSON is parsed into JavaScript data, text formats yield strings, and encoding controls binary representations. The call itself returns a Cypress chain, not the data synchronously.

Q: Why can changing a fixture file during a test still return old data?

Cypress assumes fixtures are fixed and loads them once. Later calls can use the cached content. Use cy.readFile() when a file is expected to change, or update an in-memory response object instead of rewriting the fixture.

Q: When would you use the fixture property in cy.intercept?

Use { fixture: 'accounts/active-user.json' } when the response can be served exactly as stored. Load with cy.fixture() first when the test must inspect or transform the object before registering the stub.

Q: What is the problem with accessing fixture data through this in an arrow function?

Arrow functions do not bind Mocha's test context. As a result, this.user does not refer to the alias context. Use function () {} callbacks or retrieve the alias with cy.get('@user').

Q: How do you type a JSON fixture in Cypress TypeScript?

Define the public data contract and call cy.fixture<MyType>('path.json'). The generic provides compile-time assistance but does not validate the JSON at runtime, so critical fixtures may need separate schema validation.

Q: Should a team store full production API responses as fixtures?

No. Production snapshots are often large, sensitive, and coupled to irrelevant fields. Create small synthetic payloads that represent the exact states the test needs.

Q: How do you prevent mutation from leaking between scenarios?

Treat the loaded fixture as immutable and build a new object with spread syntax or a data builder. Clone nested structures that will change, and avoid writing modifications back to the committed file.

Common Mistakes

  • Assigning cy.fixture() to a normal variable and expecting synchronous data.
  • Registering a fixture-backed intercept after cy.visit() has already triggered the request.
  • Omitting file extensions when two fixtures share the same base name.
  • Using arrow functions while reading fixture aliases through Mocha's this context.
  • Mutating a cached fixture object and allowing state to affect later work.
  • Rewriting a fixture during a test and expecting Cypress to reload it.
  • Loading large ZIP files into the browser when .selectFile() can use a path.
  • Storing real customer data, credentials, or access tokens in repository fixtures.
  • Treating a typed fixture generic as runtime validation.
  • Asserting only the stub data instead of the user-visible behavior it drives.

Conclusion

cypress cy.fixture is the right tool for small, stable, version-controlled test inputs. Load data through the Cypress chain, type it at the boundary, register API stubs before requests fire, and treat the loaded value as an immutable baseline.

Keep fixtures synthetic and purposeful. Choose cy.readFile() for changing files, .selectFile() for uploads, and cy.task() for heavy Node-side work. As a next step, review one fixture-heavy spec and remove stale fields, unsafe data, and mutations that hide the scenario's real intent.

Interview Questions and Answers

How does cy.fixture participate in the Cypress command queue?

It queues an asynchronous file read and yields the content later. It does not return the parsed object synchronously, so dependent work belongs in `.then()`, an alias chain, or a later Cypress command. This preserves execution order with browser actions.

What is the difference between cy.fixture and cy.readFile?

`cy.fixture()` represents fixed input and caches loaded content. `cy.readFile()` reads the current file state and is appropriate for outputs or files that change during a test. I choose based on whether immutability is part of the contract.

How would you avoid cross-test fixture mutation?

I treat the loaded value as immutable and create scenario variants with object spread or a focused builder. I clone nested branches that change because spread is shallow. I never write scenario changes back to the committed fixture.

When would you use a fixture-backed cy.intercept response?

I use it for deterministic frontend states such as empty data, permission errors, and specific boundary payloads. I still keep direct API or integrated tests because the stub does not prove that the real backend returns the same contract.

How should large fixture files be handled?

I reduce them to representative synthetic subsets. For uploads I pass a path to `.selectFile()`, and for large server-side file work I use `cy.task()` so bytes do not need to move into the browser runner.

What does a TypeScript generic on cy.fixture guarantee?

It guarantees only how TypeScript checks test code at compile time. It does not inspect the JSON at runtime. Critical fixture families should have a schema or runtime contract check in an appropriate fast test.

What makes a good Cypress fixture?

It is small, synthetic, stable, and named for a business state. It includes only fields needed to represent a possible contract and contains no credentials or personal data. Reviewers can understand its purpose without opening unrelated specs.

Frequently Asked Questions

What does cy.fixture do in Cypress?

It loads a file relative to the configured fixtures folder and yields its contents. JSON is parsed into JavaScript data, while text and binary formats depend on the selected encoding.

Where should Cypress fixture files be stored?

The default location is `cypress/fixtures`. You can change `fixturesFolder` in `cypress.config.ts`, but a conventional, feature-organized directory is easier for teams to maintain.

Does cy.fixture reload a file after it changes?

No. Cypress assumes fixture data is fixed and loads it once, so later calls can return cached content. Use `cy.readFile()` when the file is expected to change during the test.

How do I use a fixture with cy.intercept?

Pass `{ fixture: 'path/file.json' }` as the static response for an exact stored payload. Load it with `cy.fixture()` first when you need to inspect, clone, or transform its data.

Can Cypress fixtures contain binary files?

Yes. Cypress supports common image and archive formats, and passing `null` as the encoding yields a `Cypress.Buffer`. For an unchanged upload, prefer `.selectFile()` with the fixture path.

How can I type cy.fixture in TypeScript?

Call `cy.fixture<MyType>('path.json')` and consume the typed value in the chain. The generic is compile-time assistance and does not validate the JSON at runtime.

Why is this undefined when I read a fixture alias?

Mocha test context is not bound by arrow functions. Use `function () {}` callbacks when accessing aliases through `this`, or retrieve the alias explicitly with `cy.get('@alias')`.

Related Guides