Hardcoding test data inside test files creates the same maintenance nightmare as hardcoding locators: change a username or product name and you edit 30 files. Cypress fixtures solve this by storing test data in external JSON files inside the cypress/fixtures/ folder. Load a fixture with cy.fixture('filename') and the data flows into your test as a JavaScript object — clean, centralised and easy to update.
Loading and Using Fixture Data
Fixtures are loaded asynchronously and yielded to the next command in the chain. You can use them directly in .then() callbacks or save them as aliases for use throughout the test.
// ── Fixture file: cypress/fixtures/users.json ──
/*
{
"validUser": {
"username": "standard_user",
"password": "secret_sauce",
"expectedProducts": 6
},
"lockedUser": {
"username": "locked_out_user",
"password": "secret_sauce",
"expectedError": "Sorry, this user has been locked out"
},
"invalidUser": {
"username": "wrong_user",
"password": "wrong_pass",
"expectedError": "do not match"
}
}
*/
// ── Method 1: cy.fixture() with .then() ──
it('should login with valid credentials', () => {
cy.fixture('users').then((users) => {
cy.visit('/');
cy.get('[data-test="username"]').type(users.validUser.username);
cy.get('[data-test="password"]').type(users.validUser.password);
cy.get('[data-test="login-button"]').click();
cy.get('.inventory_item').should('have.length', users.validUser.expectedProducts);
});
});
// ── Method 2: cy.fixture() with alias (recommended for reuse) ──
describe('Login Tests', () => {
beforeEach(() => {
cy.fixture('users').as('userData');
cy.visit('/');
});
it('should login with valid credentials', function () {
// Access alias with this.userData (requires function(), not arrow =>)
cy.get('[data-test="username"]').type(this.userData.validUser.username);
cy.get('[data-test="password"]').type(this.userData.validUser.password);
cy.get('[data-test="login-button"]').click();
cy.url().should('include', '/inventory');
});
it('should show error for locked user', function () {
cy.get('[data-test="username"]').type(this.userData.lockedUser.username);
cy.get('[data-test="password"]').type(this.userData.lockedUser.password);
cy.get('[data-test="login-button"]').click();
cy.get('[data-test="error"]')
.should('contain.text', this.userData.lockedUser.expectedError);
});
});
// ── Method 3: Fixtures for network stubbing ──
// Fixture file: cypress/fixtures/products.json
/*
[
{ "id": 1, "name": "Backpack", "price": 29.99 },
{ "id": 2, "name": "Bike Light", "price": 9.99 },
{ "id": 3, "name": "T-Shirt", "price": 15.99 }
]
*/
it('should display stubbed products from fixture', () => {
// Intercept API and respond with fixture data
cy.intercept('GET', '/api/products', { fixture: 'products.json' }).as('getProducts');
cy.visit('/shop');
cy.wait('@getProducts');
cy.get('.product-card').should('have.length', 3);
cy.get('.product-card').first().should('contain.text', 'Backpack');
});
// ── Fixtures for file uploads ──
// Place test files in cypress/fixtures/
// cypress/fixtures/avatar.jpg
// cypress/fixtures/report.pdf
it('should upload a profile photo from fixtures', () => {
cy.get('input[type="file"]').selectFile('cypress/fixtures/avatar.jpg');
cy.get('.upload-preview').should('be.visible');
});
// ── Fixture organisation for large projects ──
/*
cypress/fixtures/
users/
valid-users.json
invalid-users.json
admin-users.json
products/
catalogue.json
empty-catalogue.json
api-responses/
products-200.json
products-500.json
checkout-success.json
files/
avatar.jpg
report.pdf
large-file-6mb.zip
*/
// Load nested fixture:
// cy.fixture('users/valid-users').then((users) => { ... });
this.userData, you must use a regular function() declaration — not an arrow function (=>). Arrow functions do not bind this, so this.userData would be undefined. This is a Mocha/Cypress convention: it('test', function() { this.alias }) works, but it('test', () => { this.alias }) does not. Alternatively, use cy.get('@userData') which works in both function styles.cy.intercept() to stub API responses for fast, deterministic testing. Instead of hitting a real API that might be slow or unavailable, respond with fixture data: cy.intercept('GET', '/api/products', { fixture: 'products.json' }). This makes tests run in milliseconds instead of seconds, eliminates dependency on backend availability, and lets you test error scenarios by swapping in error fixtures like products-500.json.Common Mistakes
Mistake 1 — Using arrow functions with this.alias
❌ Wrong: it('test', () => { cy.get('[data-test="username"]').type(this.userData.username); }) — this.userData is undefined in arrow functions.
✅ Correct: it('test', function() { ... this.userData ... }) or use cy.get('@userData').then((data) => { ... }) which works everywhere.
Mistake 2 — Putting all test data in a single massive fixture file
❌ Wrong: One testdata.json file with 500 lines covering users, products, addresses, and API responses.
✅ Correct: Organised fixture folders: fixtures/users/, fixtures/products/, fixtures/api-responses/. Each file is small, focused, and easy to update independently.