Skip to main content

What Are Fixtures?

Fixtures are external files that contain test data in JSON format. They provide a centralized way to manage test data, making your tests more maintainable and reusable.

Benefits of Using Fixtures

Centralized Data

Store all test data in one location for easy updates

Reusability

Share the same data across multiple tests

Separation of Concerns

Keep test data separate from test logic

Easy Maintenance

Update data once, affect all tests using it

Fixture File Structure

Fixtures are stored in the cypress/fixtures/ directory:
cypress/fixtures/
├── users.json            # User credentials
├── loginPageData.json    # UI messages and text
└── bookings.json         # API test data

Creating Fixtures

Simple Object Fixture

For single objects like user credentials:
users.json
{
  "standard": "standard_user",
  "locked": "locked_out_user",
  "problem": "problem_user",
  "performance": "performance_glitch_user",
  "error": "error_user",
  "visual": "visual_user",
  "password": "secret_sauce"
}
Usage:
cy.fixture('users').then((user: any) => {
  loginPage.typeUsername(user.standard)  // "standard_user"
  loginPage.typePassword(user.password)  // "secret_sauce"
})

Array Fixture

For collections of data:
bookings.json
[
  {
    "firstname": "James",
    "lastname": "Brown",
    "totalprice": 111,
    "depositpaid": true,
    "bookingdates": {
      "checkin": "2018-01-01",
      "checkout": "2019-01-01"
    },
    "additionalneeds": "Breakfast"
  },
  {
    "firstname": "Jim",
    "lastname": "Jones",
    "totalprice": 681,
    "depositpaid": true,
    "bookingdates": {
      "checkin": "2022-03-26",
      "checkout": "2025-03-19"
    },
    "additionalneeds": "Breakfast"
  }
]
Usage:
cy.fixture('bookings').then((bookings: any) => {
  bookings.forEach((booking, index) => {
    cy.request(`/booking/${index + 1}`).then((res) => {
      expect(res.body.firstname).to.equal(booking.firstname)
      expect(res.body.lastname).to.equal(booking.lastname)
    })
  })
})

Nested Object Fixture

For complex, structured data:
loginPageData.json
{
  "errorMsg": "Epic sadface: Sorry, this user has been locked out.",
  "successMsg": "Login successful",
  "validation": {
    "usernameRequired": "Username is required",
    "passwordRequired": "Password is required"
  }
}
Usage:
cy.fixture('loginPageData').then((data: any) => {
  loginPage.verifyErrorMessage(data.errorMsg)
  // Or access nested properties
  loginPage.verifyValidationMessage(data.validation.usernameRequired)
})

Loading Fixtures in Tests

Method 1: Using cy.fixture() with Aliases

This is the most common approach in the framework:
describe('User Authentication', () => {
  beforeEach(() => {
    // Load fixture and create alias
    cy.fixture('users').as('userData')
    cy.fixture('loginPageData').as('loginData')
    
    cy.visit('/')
  });

  it('Should log in successfully', () => {
    // Access aliased fixture
    cy.get('@userData').then((user: any) => {
      loginPage.typeUsername(user.standard)
      loginPage.typePassword(user.password)
      loginPage.clickOnLoginButton()
    })
  });
});
Using aliases (as()) makes fixtures available throughout the test. The @ prefix accesses the aliased value.

Method 2: Direct Loading

Load fixtures directly within tests:
it('Should validate user data', () => {
  cy.fixture('users').then((user: any) => {
    // Fixture is only available within this callback
    expect(user.standard).to.exist
    expect(user.password).to.equal('secret_sauce')
  })
})

Method 3: Loading Multiple Fixtures

beforeEach(() => {
  cy.fixture('users').as('userData')
  cy.fixture('loginPageData').as('loginData')
  cy.fixture('products').as('productData')
})

it('Should use multiple fixtures', () => {
  cy.get('@userData').then((user: any) => {
    cy.get('@loginData').then((login: any) => {
      cy.get('@productData').then((products: any) => {
        // Use all fixtures
      })
    })
  })
})

Real-World Examples from the Framework

Example 1: Login Tests with Multiple Users

LoginPageSpec.cy.ts
let loginPage: LoginPage
let homePage: HomePage

describe('User Authentication', () => {
  beforeEach(() => {
    cy.fixture('users').as('userData')
    loginPage = new LoginPage()
    homePage = new HomePage()
    cy.visit('/')
  });

  it('Should log in with standard user', () => {
    cy.get('@userData').then((user: any) => {
      loginPage.userLogin(user.standard, user.password)
      homePage.verifyHomePageIsVisible()
    })
  });

  it('Should log in with problem user', () => {
    cy.get('@userData').then((user: any) => {
      loginPage.userLogin(user.problem, user.password)
      homePage.verifyHomePageIsVisible()
    })
  });

  it('Should not log in with locked user', () => {
    cy.get('@userData').then((user: any) => {
      cy.get('@loginData').then((login: any) => {
        loginPage.typeUsername(user.locked)
        loginPage.typePassword(user.password)
        loginPage.clickOnLoginButton()
        loginPage.verifyErrorMessage(login.errorMsg)
      })
    })
  });
});

Example 2: API Tests with Expected Data

GETSpec.cy.ts
describe('API Requests - GET', () => {
  it('Should return specific booking data', () => {
    cy.fixture('bookings').then((bookingData: any) => {
      for (let i = 1; i < 4; i++) {
        cy.request({
          method: 'GET',
          url: `/booking/${i}`,
        }).then((res) => {
          const { firstname, lastname, totalprice, depositpaid } = res.body
          expect(firstname).to.equal(bookingData[i - 1].firstname)
          expect(lastname).to.equal(bookingData[i - 1].lastname)
          expect(totalprice).to.equal(bookingData[i - 1].totalprice)
          expect(depositpaid).to.equal(bookingData[i - 1].depositpaid)
        })
      }
    })
  })
});

Fixture Organization Strategies

Strategy 1: By Feature

Organize fixtures by application feature:
cypress/fixtures/
├── auth/
   ├── users.json
   └── credentials.json
├── products/
   ├── inventory.json
   └── pricing.json
└── checkout/
    └── payment-methods.json
Usage:
cy.fixture('auth/users').as('userData')
cy.fixture('products/inventory').as('products')

Strategy 2: By Test Type

Separate UI and API test data:
cypress/fixtures/
├── ui/
   ├── users.json
   └── messages.json
└── api/
    ├── bookings.json
    └── responses.json

Strategy 3: By Environment

Different data for different environments:
cypress/fixtures/
├── dev/
   └── users.json
├── staging/
   └── users.json
└── prod/
    └── users.json

Advanced Fixture Patterns

Pattern 1: Dynamic Data with Functions

Generate dynamic data at runtime:
it('Should create booking with current date', () => {
  cy.fixture('bookings').then((booking: any) => {
    const today = new Date().toISOString().split('T')[0]
    const checkoutDate = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
      .toISOString().split('T')[0]

    cy.request({
      method: 'POST',
      url: '/booking',
      body: {
        ...booking[0],
        bookingdates: {
          checkin: today,
          checkout: checkoutDate
        }
      }
    })
  })
})

Pattern 2: Fixture Merging

Combine multiple fixtures:
it('Should use merged data', () => {
  cy.fixture('users').then((users: any) => {
    cy.fixture('addresses').then((addresses: any) => {
      const userData = {
        ...users.standard,
        address: addresses.home
      }
      // Use merged data
    })
  })
})

Pattern 3: Parameterized Tests with Fixtures

it('Should login with multiple users', () => {
  cy.fixture('users').then((user: any) => {
    const userTypes = ['standard', 'problem', 'performance', 'visual']
    
    userTypes.forEach(userType => {
      cy.visit('/')
      loginPage.userLogin(user[userType], user.password)
      homePage.verifyHomePageIsVisible()
      navbar.userLogout()
    })
  })
})

TypeScript Type Safety with Fixtures

Create interfaces for type-safe fixtures:
types.ts
interface UserCredentials {
  standard: string
  locked: string
  problem: string
  performance: string
  error: string
  visual: string
  password: string
}

interface Booking {
  firstname: string
  lastname: string
  totalprice: number
  depositpaid: boolean
  bookingdates: {
    checkin: string
    checkout: string
  }
  additionalneeds?: string
}
Usage with types:
it('Should use typed fixtures', () => {
  cy.fixture<UserCredentials>('users').then((user) => {
    // TypeScript knows the structure
    loginPage.typeUsername(user.standard)
    loginPage.typePassword(user.password)
  })
})

Fixture Best Practices

Don’t over-complicate fixture structure:
// ✅ Good - simple and clear
{
  "standard": "standard_user",
  "password": "secret_sauce"
}

// ❌ Bad - overly nested
{
  "users": {
    "valid": {
      "primary": {
        "main": "standard_user"
      }
    }
  }
}
Make it clear what each property represents:
// ✅ Good
{
  "errorMsg": "Epic sadface: Sorry, this user has been locked out."
}

// ❌ Bad
{
  "msg1": "Epic sadface: Sorry, this user has been locked out."
}
Don’t store real credentials or sensitive information:
// ✅ Good - test credentials
{
  "username": "test_user",
  "password": "test_pass_123"
}

// ❌ Bad - real credentials
{
  "username": "[email protected]",
  "password": "MyRealPassword123!"
}
Commit fixtures to version control since they’re test code:
# ✅ Include in git
git add cypress/fixtures/*.json

# ❌ Don't exclude unless they contain secrets
# .gitignore
# cypress/fixtures/*.json  ← Usually wrong

Common Issues and Solutions

Error: cy.fixture() failed because the fixture does not existSolution: Verify the file path and name:
// Check file exists at: cypress/fixtures/users.json
cy.fixture('users')  // No .json extension needed

// For subdirectories
cy.fixture('auth/users')
Error: Accessing fixture data outside the .then() callbackSolution: Access data within the callback:
// ❌ Bad
cy.fixture('users').as('userData')
const user = cy.get('@userData')  // Won't work

// ✅ Good
cy.get('@userData').then((user: any) => {
  // Access user here
})
Issue: Changes to fixture files not reflected in testsSolution: Cypress caches fixtures. Reload the test or restart Cypress:
# Close and reopen Cypress Test Runner
# Or in headless mode, just re-run
npm run cy:run:ui

Fixtures vs Environment Variables

Use Fixtures For

  • Test data
  • Expected results
  • User information
  • API payloads

Use Environment Variables For

  • Base URLs
  • API keys
  • Configuration settings
  • Sensitive credentials
// ✅ Fixture for test data
cy.fixture('users').then((user) => {
  loginPage.typeUsername(user.standard)
})

// ✅ Environment variable for sensitive data
const apiKey = Cypress.env('API_KEY')
cy.request({
  url: '/api/data',
  headers: { 'Authorization': `Bearer ${apiKey}` }
})

Next Steps

Writing UI Tests

Apply fixtures in your UI tests

Writing API Tests

Use fixtures for API test data

Test Organization

Organize fixtures with your tests

Maintainability

Keep fixtures maintainable

Build docs developers (and LLMs) love