QA How-To
Cypress cy.fixture: Examples
Explore each cypress cy.fixture example for API stubs, forms, aliases, typed variants, CSV, binary uploads, component tests, and contract checks in 2026.
20 min read | 2,328 words
TL;DR
A strong cypress cy.fixture example loads purposeful synthetic data, feeds it to the application, and asserts a visible or network outcome. Use fixture shortcuts for exact responses and clone loaded data for controlled variants.
Key Takeaways
- Connect fixture data to application behavior instead of stopping at a direct file-content assertion.
- Register fixture-backed intercepts before page load when the application sends startup requests.
- Create small in-memory variants from a baseline instead of multiplying nearly identical fixture files.
- Use static imports rather than cy.fixture when JSON rows must define separate Mocha tests synchronously.
- Upload existing files with selectFile paths and load buffers only when contents or metadata must change.
- Add one fast contract check per important fixture family to prevent mocked UI tests from hiding stale payloads.
A practical cypress cy.fixture example should show more than reading a JSON file. The fixture must drive a meaningful test action, such as stubbing an API, filling a form, uploading a document, or checking an edge state, while the test asserts what a user can observe.
This example-focused guide builds a reusable set of Cypress patterns for JSON, arrays, aliases, typed data, response variants, files, and component tests. Each recipe explains when to use it and what can make it misleading, so you can adapt the code without inheriting brittle test-data habits.
TL;DR
type Product = {
id: string
name: string
price: number
stock: number
}
cy.fixture<Product[]>('catalog/products.json').then((products) => {
cy.intercept('GET', '/api/products', {
statusCode: 200,
body: products,
}).as('getProducts')
})
cy.visit('/products')
cy.wait('@getProducts')
cy.get('[data-cy="product-card"]').should('have.length', 3)
| Example goal | Recommended pattern |
|---|---|
| Use exact stored response | cy.intercept(..., { fixture: 'file.json' }) |
| Change one field | Load, clone, then use { body } |
| Share data in one test | .as() plus cy.get('@alias') |
| Generate many variants | Data builder based on a small fixture |
| Upload a file | .selectFile() with fixture path |
| Observe a changing output file | cy.readFile() |
1. Build the First cypress cy.fixture example
Create a fixture at cypress/fixtures/catalog/products.json:
[
{ "id": "coffee-01", "name": "Precision Grinder", "price": 129, "stock": 8 },
{ "id": "scale-02", "name": "Brew Scale", "price": 49, "stock": 15 },
{ "id": "filter-03", "name": "Steel Filter", "price": 24, "stock": 0 }
]
Then load the file and make a direct data assertion:
describe('product fixture', () => {
it('contains the catalog states required by the suite', () => {
cy.fixture('catalog/products.json').then((products) => {
expect(products).to.have.length(3)
expect(products.map((product: { stock: number }) => product.stock))
.to.include(0)
})
})
})
This example proves that Cypress can find and parse the fixture, but it is not yet a strong user test. The application has not consumed the data. Use this type of check as a small fixture contract test, not as the main acceptance scenario.
The path is relative to fixturesFolder, which is cypress/fixtures by default. Including .json makes the dependency explicit. The yielded value is available inside .then() because cy.fixture() participates in Cypress's command queue.
A better next step is to feed the array to the browser through a network stub and assert the resulting UI. That tests the product behavior while retaining deterministic input.
2. Stub a GET Response from a JSON Fixture
Register cy.intercept() before navigation because many applications request their initial data during page load. Use the fixture shortcut when no transformation is required.
describe('catalog', () => {
it('renders products and out-of-stock state', () => {
cy.intercept('GET', '/api/products', {
fixture: 'catalog/products.json',
}).as('getProducts')
cy.visit('/products')
cy.wait('@getProducts').then(({ response }) => {
expect(response?.statusCode).to.eq(200)
})
cy.get('[data-cy="product-card"]').should('have.length', 3)
cy.contains('[data-cy="product-card"]', 'Steel Filter')
.should('contain.text', 'Out of stock')
})
})
The alias makes the test synchronize with a real application event. It is better than cy.wait(2000), which guesses how long the request and rendering will take. The final assertions target the interface, not merely the intercepted body.
This test is deterministic, but it does not verify the live products service. Keep direct API checks or a smaller integrated flow for that responsibility. Stubbing is valuable when testing rendering, empty states, errors, and client logic that would otherwise be difficult to reproduce.
For route matching details, see the Cypress cy.intercept reference. A narrow method and URL prevent an unrelated request from consuming the alias.
3. Modify a Baseline Fixture for One Scenario
Create variants in memory when one or two fields change. This keeps the baseline readable without multiplying files such as product1.json, product1-new.json, and product1-final.json.
type Product = {
id: string
name: string
price: number
stock: number
}
it('disables purchase when every product is unavailable', () => {
cy.fixture<Product[]>('catalog/products.json').then((products) => {
const unavailableProducts = products.map((product) => ({
...product,
stock: 0,
}))
cy.intercept('GET', '/api/products', {
statusCode: 200,
body: unavailableProducts,
}).as('getProducts')
})
cy.visit('/products')
cy.wait('@getProducts')
cy.contains('button', 'Add to cart').should('be.disabled')
})
Notice that the code maps to new objects instead of editing the cached fixture array. Treating fixture data as an immutable baseline reduces cross-test surprises.
Use a separate fixture when a state has a different shape or business meaning, such as a permission error envelope versus a successful product list. In-memory transformation is ideal for a small variation, not for constructing a completely different contract through twenty lines of mutations.
If nested data changes, clone the nested branch too. { ...product } is shallow. A test that writes variant.seller.address.country = 'CA' can still mutate the nested object shared with product.
4. Fill a Form from Fixture Data
Fixtures can hold reusable form input, but selectors and assertions should still describe user behavior. Consider cypress/fixtures/accounts/new-account.json:
{
"name": "Avery Chen",
"email": "avery.chen@example.test",
"company": "Quality Works",
"plan": "team"
}
The test fills each field and verifies the submitted request:
it('creates an account with fixture-backed form data', () => {
cy.fixture('accounts/new-account.json').then((account) => {
cy.intercept('POST', '/api/accounts').as('createAccount')
cy.visit('/accounts/new')
cy.get('input[name="name"]').type(account.name)
cy.get('input[name="email"]').type(account.email)
cy.get('input[name="company"]').type(account.company)
cy.get('select[name="plan"]').select(account.plan)
cy.contains('button', 'Create account').click()
cy.wait('@createAccount').its('request.body').should('deep.include', account)
cy.contains('[role="status"]', 'Account created').should('be.visible')
})
})
This is useful when the same realistic profile supports multiple scenarios. Avoid building a universal form fixture with every optional field. It hides which inputs matter and makes tests change when unrelated fields are added.
For validation tests, inline a small invalid value when that makes the intent clearer. cy.get(...).type('not-an-email') is more readable than opening invalid-user-17.json just to discover one malformed field.
Never use a real person's contact data. The reserved .test domain makes the synthetic nature explicit and prevents accidental mail delivery.
5. Use Aliases in beforeEach Without this Problems
An alias is useful when several tests in a context consume the same baseline. Retrieve it with cy.get() to avoid Mocha context and arrow-function confusion.
type AccountInput = {
name: string
email: string
company: string
plan: string
}
describe('account form', () => {
beforeEach(() => {
cy.fixture<AccountInput>('accounts/new-account.json').as('accountInput')
})
it('shows entered company name in the review', () => {
cy.get<AccountInput>('@accountInput').then((account) => {
cy.visit('/accounts/new')
cy.get('input[name="company"]').type(account.company)
cy.contains('button', 'Review').click()
cy.get('[data-cy="company-review"]').should('have.text', account.company)
})
})
})
Aliases reset before each test, so the hook needs to run for every scenario. Cypress aliases can be queried later in the command queue, and the generic adds editor support.
The alternative is Mocha's this context:
beforeEach(function () {
cy.fixture('accounts/new-account.json').as('accountInput')
})
it('reads the alias', function () {
expect(this.accountInput.plan).to.eq('team')
})
That works only with function () {} callbacks. Choose one style consistently. Explicit alias retrieval is often easier to refactor and does not require extending the test context type.
6. Parameterize Tests from a Fixture Array
A fixture array can describe several inputs, but Cypress commands cannot generate test definitions after the suite has started running. Test cases are registered synchronously when the spec loads, while cy.fixture() resolves later. This pattern does not work:
// Incorrect: it() cannot be created after cy.fixture resolves.
cy.fixture('search/cases.json').then((cases) => {
cases.forEach((testCase: unknown) => {
it('runs a case', () => {})
})
})
If the goal is one Cypress test that checks several rows, iterate within the command chain:
it('shows expected results for fixture search cases', () => {
cy.fixture<Array<{ query: string; expected: string }>>('search/cases.json')
.then((cases) => {
cy.wrap(cases).each((testCase) => {
cy.visit('/search')
cy.get('input[type="search"]').type(testCase.query)
cy.contains('button', 'Search').click()
cy.get('[data-cy="search-result"]').should('contain.text', testCase.expected)
})
})
})
One test means one failure can stop remaining cases. For independent reporting, export static JavaScript or TypeScript test-case data from a module and call it() in a synchronous loop. Use JSON fixtures primarily for runtime data, not dynamic suite construction.
Keep parameter sets small and purposeful. A browser test with hundreds of rows is slow and hard to diagnose. Push broad combinatorial validation into unit or API tests, then select representative UI cases.
7. Test Empty, Error, and Boundary Responses
Edge states deserve explicit examples. A tiny fixture often communicates them better than conditional server setup.
{
"items": [],
"page": 1,
"total": 0
}
it('shows an empty catalog message', () => {
cy.intercept('GET', '/api/products*', {
fixture: 'catalog/empty-page.json',
}).as('getEmptyPage')
cy.visit('/products')
cy.wait('@getEmptyPage')
cy.contains('No products found').should('be.visible')
cy.get('[data-cy="product-card"]').should('not.exist')
})
For a standard error envelope, store the response body in a fixture and set the HTTP status separately:
cy.intercept('GET', '/api/products*', {
statusCode: 503,
fixture: 'errors/service-unavailable.json',
headers: { 'retry-after': '30' },
}).as('getProductsError')
Do not use an error fixture with a default 200 response unless the application contract really encodes errors that way. Status, headers, and body jointly define the response.
Boundary payloads should be valid examples near a business limit, not huge copies. A product name at the supported maximum length and a price at zero express more intent than a random production catalog. The boundary value analysis guide can help select cases without bloating the fixture directory.
8. Upload Files from the Fixtures Folder
For a normal file input, use .selectFile() with a fixture path. You do not need to call cy.fixture() first.
it('uploads a valid avatar', () => {
cy.intercept('POST', '/api/profile/avatar').as('uploadAvatar')
cy.visit('/profile')
cy.get('input[type="file"]')
.selectFile('cypress/fixtures/files/valid-avatar.png')
cy.contains('button', 'Upload').click()
cy.wait('@uploadAvatar').its('response.statusCode').should('eq', 201)
cy.get('[data-cy="avatar-preview"]').should('be.visible')
})
Load a fixture as null encoding only when you must inspect or construct file contents:
cy.fixture('files/valid-avatar.png', null).then((contents) => {
cy.get('input[type="file"]').selectFile({
contents,
fileName: 'avatar.png',
mimeType: 'image/png',
lastModified: Date.now(),
})
})
The object form is helpful for generated names, MIME-type cases, or buffer transformations. The path form is leaner for an unchanged file and avoids unnecessary browser-side handling.
For drag-and-drop components, pass { action: 'drag-drop' } to .selectFile(). Assert the uploaded file's user-visible status and the network outcome. Merely checking that the input has a value does not prove server processing succeeded.
9. Read Text, CSV, and Base64 Examples
Text fixtures are useful for import data, templates, and expected snippets. Cypress yields a string when using text encodings.
it('imports a small CSV fixture', () => {
cy.fixture('imports/accounts.csv', 'utf8').then((csv) => {
expect(csv.split('\n')[0]).to.eq('name,email,status')
})
cy.visit('/imports')
cy.get('input[type="file"]')
.selectFile('cypress/fixtures/imports/accounts.csv')
cy.contains('button', 'Import').click()
cy.contains('3 accounts imported').should('be.visible')
})
For an image that must be embedded in a request, base64 can be appropriate:
cy.fixture('files/signature.png', 'base64').then((base64Image) => {
cy.request('POST', '/api/signatures', {
fileName: 'signature.png',
data: base64Image,
}).its('status').should('eq', 201)
})
Confirm that the API actually expects base64. Sending base64 where multipart form data is required tests the wrong protocol. Likewise, avoid manually parsing complex CSV in an end-to-end spec. The product should parse the uploaded file, while a dedicated lower-level test can validate the parser across many cases.
When exact line endings matter, be explicit about normalization. CI may run on a different operating system than local development. Keep fixture encoding UTF-8 unless the scenario specifically tests another encoding.
10. Use Fixtures in Cypress Component Tests
Component tests can supply fixture data as props or stub network calls used by the mounted component. Passing props directly is fastest when the component owns presentation behavior.
import ProductList from './ProductList'
describe('<ProductList />', () => {
it('renders fixture-backed products', () => {
cy.fixture<Product[]>('catalog/products.json').then((products) => {
cy.mount(<ProductList products={products} />)
})
cy.get('[data-cy="product-card"]').should('have.length', 3)
cy.contains('Steel Filter').should('be.visible')
})
})
If the component fetches its own data, intercept before mounting:
it('renders a service failure', () => {
cy.intercept('GET', '/api/products', {
statusCode: 503,
fixture: 'errors/service-unavailable.json',
}).as('getProducts')
cy.mount(<ProductPage />)
cy.wait('@getProducts')
cy.contains('Products are temporarily unavailable').should('be.visible')
})
Choose the boundary that belongs to the test. A pure list component does not need a network stub if props are its public API. A page-level component that coordinates loading and error states benefits from an intercepted request.
Do not share mutable fixture objects between mounted components. Create a fresh variant inside each test, and let Cypress reset the mounted application state.
11. Build an Advanced cypress cy.fixture example Safely
A maintainable suite often combines a baseline fixture with a typed builder. The fixture keeps representative shape visible, while the builder expresses small scenario changes.
type Order = {
id: string
state: 'draft' | 'paid' | 'cancelled'
total: number
customer: { name: string; tier: 'standard' | 'gold' }
}
const buildOrder = (base: Order, changes: Partial<Order> = {}): Order => ({
...base,
...changes,
customer: {
...base.customer,
...(changes.customer ?? {}),
},
})
it('shows priority support for a paid gold order', () => {
cy.fixture<Order>('orders/baseline.json').then((base) => {
const order = buildOrder(base, {
state: 'paid',
customer: { ...base.customer, tier: 'gold' },
})
cy.intercept('GET', `/api/orders/${order.id}`, { body: order }).as('getOrder')
cy.visit(`/orders/${order.id}`)
cy.wait('@getOrder')
cy.contains('Priority support').should('be.visible')
})
})
Do not turn builders into a second application with defaults no one understands. Keep a reviewed baseline file, make changes explicit at the test site, and avoid random data unless the random seed and failing values are captured.
If a scenario needs database state, use an API or Node task designed for setup rather than assuming a UI response fixture creates backend records. Fixtures shape test input, they do not provide environment lifecycle management.
12. Validate Fixture Contracts and Keep Examples Maintainable
A fixture can become stale while every stubbed UI test remains green. The frontend receives exactly what the test supplies, so an outdated field name or enum may never meet the real service. Add a small contract check at the appropriate layer instead of pretending every end-to-end example proves integration.
One lightweight option is a dedicated fixture shape test:
type Product = {
id: string
name: string
price: number
stock: number
}
const assertProduct = (value: unknown): asserts value is Product => {
expect(value).to.have.all.keys('id', 'name', 'price', 'stock')
expect(value).to.have.property('id').that.is.a('string')
expect(value).to.have.property('name').that.is.a('string')
expect(value).to.have.property('price').that.is.a('number')
expect(value).to.have.property('stock').that.is.a('number')
}
it('keeps the catalog fixture on its public contract', () => {
cy.fixture<unknown[]>('catalog/products.json').then((products) => {
expect(products).to.be.an('array').and.not.be.empty
products.forEach(assertProduct)
})
})
For a larger API, use the same JSON Schema or runtime schema that direct API tests use. Run that validation once per fixture family, not redundantly in every UI case. The UI scenario should stay focused on rendering and behavior.
Name fixtures by stable business state and keep them close to the feature hierarchy. Search for references before removing a file. A fixture referenced through { fixture: '...' } is still a dependency even though no cy.fixture() call names it.
In pull requests, review three connected changes together: the fixture fields, the intercept that serves them, and the user assertion. Ask whether the payload represents a possible service response, whether the scenario needs every field, and whether the assertion would fail if the intended behavior broke. This prevents examples from becoming decorative data setup.
Avoid snapshot-style updates in which a team replaces a fixture wholesale until tests pass. When a contract changes, edit the smallest relevant fields, update direct API validation, and explain the product behavior that required the change. A deterministic fixture should make test intent easier to audit, not conceal it behind a large JSON diff.
Also decide which layer owns default values. If the service supplies status: 'active', include it in the fixture and assert the UI behavior that depends on it. If the frontend supplies a visual default only when the field is absent, create an explicit missing-field case. Silently adding defaults in a test builder can make an impossible response look valid and can hide defensive code that is never exercised.
Run fixture contract checks early in CI, before the full browser matrix. A fast failure with a filename and missing property is far easier to diagnose than several page assertions failing later. This small investment keeps the cookbook examples trustworthy as service contracts evolve.
Interview Questions and Answers
Q: Show a cypress cy.fixture example that stubs an API.
Register cy.intercept('GET', '/api/products', { fixture: 'catalog/products.json' }).as('getProducts') before visiting the page. Trigger the request, wait on @getProducts, and assert the rendered products. This combines deterministic data with user-visible verification.
Q: How do you change one property without modifying the fixture?
Load the object in .then(), create a new object with spread syntax, and pass the new value as the intercept body. Clone any nested branch that changes because object spread is shallow.
Q: Can cy.fixture generate separate it blocks from a JSON array?
Not after the command resolves. Cypress and Mocha register tests synchronously, while cy.fixture() runs later in the command queue. Import static test data for separate cases, or iterate within one already-defined test.
Q: How do you upload a fixture file?
Use .selectFile('cypress/fixtures/path/file.png') on the file input. Load a buffer with cy.fixture(path, null) only if you need to alter contents or supply custom metadata.
Q: Why should an intercept be registered before cy.visit?
Applications often send startup requests immediately. Registering later creates a race in which the request reaches the server before Cypress can match it, and cy.wait() then times out.
Q: How would you share fixture data across tests?
Load it in beforeEach and assign an alias, then retrieve it with cy.get('@alias') inside each test. Do not expect aliases or mutated objects to persist safely across isolated tests.
Q: When is an inline object clearer than a fixture?
Use an inline object for a tiny, single-use payload whose meaning is obvious at the assertion site. Use a fixture for larger representative data reused across scenarios or file formats that must exist on disk.
Common Mistakes
- Creating test definitions inside
cy.fixture().then()after suite registration. - Visiting the page before registering a startup request intercept.
- Testing only fixture contents without showing that the application consumed them.
- Mutating a cached object instead of creating a scenario-specific copy.
- Using one huge universal JSON file for unrelated features and states.
- Loading an upload file into memory when
.selectFile()can accept its path. - Supplying a fixture error body with the wrong HTTP status.
- Using production emails, IDs, tokens, or documents as convenient sample data.
- Relying on TypeScript types as proof that JSON is valid at runtime.
- Running hundreds of fixture rows through a slow browser loop.
Conclusion
The best cypress cy.fixture example connects fixed data to behavior: serve a response, fill inputs, mount a component, or upload a file, then assert the result a user or downstream service can observe. Keep the fixture small, synthetic, typed where useful, and immutable during the scenario.
Start with the simplest recipe that fits the boundary. Use the intercept fixture shortcut for exact responses, load and clone for variants, choose .selectFile() for uploads, and move broad data combinations to faster test layers.
Interview Questions and Answers
Give an example of using cy.fixture with cy.intercept.
I register `cy.intercept('GET', '/api/products', { fixture: 'catalog/products.json' }).as('getProducts')` before visiting. Then I wait for the alias and assert the rendered product state. This makes the input deterministic while keeping the assertion user-focused.
How would you create an out-of-stock variant from a fixture?
I load the product array and map it to new objects with `stock: 0`. I pass the new array as the intercept body and leave the cached baseline untouched. If nested data changes, I clone that branch explicitly.
Why can test definitions not be created inside cy.fixture().then()?
Mocha discovers and registers tests synchronously when the spec is evaluated. `cy.fixture()` runs later in the Cypress command queue. I use a static module for generated test definitions or loop over runtime data inside one test.
What would you assert in a form fixture example?
I assert the request contains fields the UI owns and that the user sees the correct success or validation state. Merely confirming that inputs received fixture strings does not prove the form workflow succeeded.
When should a fixture be replaced with inline data?
A tiny single-use payload is often clearer inline because the scenario remains visible at the assertion site. I use fixtures for reusable representative structures, larger payloads, or formats that must exist as real files.
How do aliases help with fixture examples?
An alias lets a later Cypress command retrieve the yielded data within a test. I load it in `beforeEach` when each test needs the same baseline and use `cy.get('@alias')` to avoid Mocha `this` binding issues.
How would you validate a fixture contract?
I run a dedicated fast check that parses the fixture and validates required properties, types, and allowed values through a schema or runtime validator. That check complements, rather than bloats, each UI scenario.
Frequently Asked Questions
What is a simple Cypress cy.fixture example?
Call `cy.fixture('catalog/products.json').then((products) => { ... })` and use the yielded data inside the chain. A stronger test passes the products to `cy.intercept()` and verifies the rendered catalog.
Can I change fixture data for one test?
Yes. Load the baseline, create a new object or array with spread and mapping, and use that variant as an intercept body. Avoid mutating the cached baseline itself.
Can cy.fixture create parameterized it blocks?
Not after the command resolves because Mocha registers tests synchronously. Use an imported static module for independent generated cases, or iterate over fixture rows inside one already-defined test.
How do I use a fixture in a Cypress component test?
Load the data, then mount the component with the fixture as props, or register an intercept before mounting a component that fetches its own data. Choose the component public boundary the test is meant to exercise.
How do I upload a fixture file in Cypress?
Call `.selectFile('cypress/fixtures/files/name.png')` on the file input. Use the object form with a buffer only when you need a custom filename, MIME type, timestamp, or modified contents.
Should empty and error responses use separate fixtures?
Use separate fixtures when they represent distinct shapes or stable business states. A one-field variant can be built in memory, but a standard error envelope usually deserves an explicit file plus the correct HTTP status.
How do I keep fixture examples aligned with the real API?
Validate important fixture families against the public schema or a runtime contract in a fast dedicated test. Keep some integrated API or end-to-end coverage because stubs alone cannot detect server drift.