LearnThatStack Ace your next interview
Testing · Free

Cypress.
Interview cheat sheet.

Quick reference for Cypress - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Testing 16-section reference ~6 min read

Summary

Cypress is a modern JavaScript-based end-to-end testing framework designed specifically for testing web applications. Unlike traditional testing tools, Cypress runs directly in the browser's execution loop, providing automatic waiting, real-time reloading, and time-travel debugging. It offers a complete testing solution with built-in assertions, mocking capabilities, and an intuitive API that makes writing and debugging tests faster and more reliable. This cheatsheet covers Cypress fundamentals, best practices, and advanced patterns for technical interviews.

Installation & Setup

# Install Cypress
npm install --save-dev cypress

# Open Cypress
npx cypress open

# Run tests headlessly
npx cypress run

Project Structure

cypress/
├── e2e/           # Test files
├── fixtures/      # Test data
├── support/       # Custom commands & utilities
└── downloads/     # Downloaded files during tests

Configuration (cypress.config.js)

module.exports = {
  e2e: {
    baseUrl: 'http://localhost:3000',
    viewportWidth: 1280,
    viewportHeight: 720,
    defaultCommandTimeout: 4000,
    requestTimeout: 5000,
    responseTimeout: 30000
  }
}

Basic Commands

cy.visit('https://example.com')
cy.visit('/login')  // Uses baseUrl
cy.go('back')
cy.go('forward')
cy.reload()

Element Selection

cy.get('.class')           // By class
cy.get('#id')              // By ID
cy.get('[data-cy=submit]') // By data attribute
cy.get('button').first()   // First element
cy.get('li').last()        // Last element
cy.get('tr').eq(2)         // By index

Waiting

cy.wait(1000)              // Wait 1 second (avoid if possible)
cy.wait('@apiCall')        // Wait for intercepted request
cy.wait(['@call1', '@call2']) // Multiple waits

Selectors & Queries

Chaining Commands

cy.get('form')
  .find('input[type="email"]')
  .type('user@example.com')

cy.get('ul').children()      // Direct children
cy.get('li').parent()        // Parent element
cy.get('button').siblings()  // Sibling elements
cy.get('div').closest('form') // Closest ancestor

Filtering

cy.get('button').filter('.active')
cy.get('li').not('.disabled')
cy.get('div').contains('Submit')  // Contains text
cy.get('input').filter(':visible')

Aliases

cy.get('.my-element').as('myAlias')
cy.get('@myAlias').click()

Assertions

Common Assertions

// Existence
cy.get('button').should('exist')
cy.get('.error').should('not.exist')

// Visibility
cy.get('modal').should('be.visible')
cy.get('spinner').should('not.be.visible')

// Text content
cy.get('h1').should('have.text', 'Welcome')
cy.get('p').should('contain', 'Hello')
cy.get('div').should('not.contain', 'Error')

// Attributes
cy.get('input').should('have.attr', 'placeholder', 'Email')
cy.get('button').should('have.class', 'btn-primary')
cy.get('a').should('have.css', 'color', 'rgb(255, 0, 0)')

// State
cy.get('button').should('be.disabled')
cy.get('input').should('be.enabled')
cy.get('checkbox').should('be.checked')
cy.get('option').should('be.selected')

// Value
cy.get('input').should('have.value', 'test@example.com')

// Length
cy.get('li').should('have.length', 5)
cy.get('li').should('have.length.greaterThan', 3)

Multiple Assertions

cy.get('button')
  .should('be.visible')
  .and('be.enabled')
  .and('contain', 'Submit')

Actions & Interactions

Form Interactions

// Typing
cy.get('input').type('Hello World')
cy.get('input').type('{enter}')  // Special keys
cy.get('input').clear().type('New text')

// Clicking
cy.get('button').click()
cy.get('button').dblclick()
cy.get('button').rightclick()
cy.get('button').click({ force: true })  // Force click

// Selecting
cy.get('select').select('option-value')
cy.get('select').select(['opt1', 'opt2'])  // Multiple

// Checkboxes & Radio
cy.get('input[type="checkbox"]').check()
cy.get('input[type="checkbox"]').uncheck()
cy.get('input[type="radio"]').check('value')

Mouse Actions

cy.get('element').trigger('mouseover')
cy.get('element').trigger('mousedown')
cy.get('draggable').trigger('dragstart')

Scrolling

cy.scrollTo(0, 500)              // Scroll window
cy.get('.sidebar').scrollTo('bottom')
cy.get('button').scrollIntoView()

Hooks & Test Structure

describe('Test Suite', () => {
  before(() => {
    // Runs once before all tests
  })

  beforeEach(() => {
    // Runs before each test
    cy.visit('/')
  })

  afterEach(() => {
    // Runs after each test
  })

  after(() => {
    // Runs once after all tests
  })

  it('should do something', () => {
    // Test case
  })

  it.only('runs only this test', () => {
    // Focused test
  })

  it.skip('skips this test', () => {
    // Skipped test
  })
})

Custom Commands

Creating Custom Commands

// In cypress/support/commands.js
Cypress.Commands.add('login', (email, password) => {
  cy.get('#email').type(email)
  cy.get('#password').type(password)
  cy.get('button[type="submit"]').click()
})

// Usage
cy.login('user@example.com', 'password123')

Overwriting Commands

Cypress.Commands.overwrite('type', (originalFn, element, text, options) => {
  if (options && options.sensitive) {
    options.log = false
  }
  return originalFn(element, text, options)
})

Fixtures & Test Data

Using Fixtures

// cypress/fixtures/user.json
{
  "email": "test@example.com",
  "password": "password123"
}

// In test
cy.fixture('user').then((user) => {
  cy.get('#email').type(user.email)
  cy.get('#password').type(user.password)
})

// Or using beforeEach
beforeEach(function() {
  cy.fixture('user').as('userData')
})

it('uses fixture data', function() {
  cy.get('#email').type(this.userData.email)
})

Network Requests & Intercepting

Intercepting Requests (cy.intercept)

// Basic intercept
cy.intercept('GET', '/api/users', { fixture: 'users.json' })

// With alias
cy.intercept('POST', '/api/login').as('loginRequest')
cy.get('button').click()
cy.wait('@loginRequest')

// Dynamic response
cy.intercept('GET', '/api/user/*', (req) => {
  req.reply({
    statusCode: 200,
    body: { name: 'Test User' }
  })
})

// Modifying requests
cy.intercept('POST', '/api/data', (req) => {
  req.body.timestamp = Date.now()
})

// Conditional stubbing
cy.intercept('GET', '/api/data', (req) => {
  if (req.query.type === 'error') {
    req.reply({ statusCode: 500 })
  }
})

Assertions on Requests

cy.wait('@apiCall').then((interception) => {
  expect(interception.request.body).to.have.property('email')
  expect(interception.response.statusCode).to.eq(200)
})

Best Practices

1. Use Data Attributes

<button data-cy="submit-button">Submit</button>
cy.get('[data-cy=submit-button]').click()

2. Avoid Hard-Coded Waits

// ❌ Bad
cy.wait(3000)

// ✅ Good
cy.get('.spinner').should('not.exist')
cy.intercept('/api/data').as('getData')
cy.wait('@getData')

3. Keep Tests Independent

// Each test should be able to run in isolation
beforeEach(() => {
  cy.visit('/')
  // Reset state
})

4. Use Aliases for Reusability

cy.get('[data-cy=user-list]').as('userList')
cy.get('@userList').should('be.visible')
cy.get('@userList').find('li').should('have.length', 3)

5. Handle Flaky Tests

// Retry ability
cy.get('button', { timeout: 10000 }).should('be.visible')

// Retry configuration
{
  retries: {
    runMode: 2,
    openMode: 0
  }
}

Advanced Topics

Environment Variables

// cypress.config.js
module.exports = {
  env: {
    apiUrl: 'https://api.example.com',
    auth_token: 'xyz123'
  }
}

// Usage
cy.request(Cypress.env('apiUrl') + '/users')

Viewport Testing

// Test different screen sizes
cy.viewport(320, 568)  // iPhone 5
cy.viewport('iphone-x')
cy.viewport(1280, 720)

// In config
viewportWidth: 1000,
viewportHeight: 660

File Upload

cy.get('input[type="file"]').selectFile('cypress/fixtures/image.png')
cy.get('input[type="file"]').selectFile({
  contents: Cypress.Buffer.from('file contents'),
  fileName: 'file.txt',
  mimeType: 'text/plain'
})

Working with iframes

cy.get('iframe').then($iframe => {
  const body = $iframe.contents().find('body')
  cy.wrap(body).find('button').click()
})

Multiple Tabs/Windows

// Cypress runs in a single tab, so remove target="_blank"
cy.get('a[target="_blank"]').invoke('removeAttr', 'target').click()

Session Management

// Cache session across tests
Cypress.Commands.add('loginSession', (username, password) => {
  cy.session([username, password], () => {
    cy.visit('/login')
    cy.get('#username').type(username)
    cy.get('#password').type(password)
    cy.get('button').click()
    cy.url().should('include', '/dashboard')
  })
})

Key Interview Concepts

Cypress vs Selenium Comparison

Feature Cypress Selenium
Architecture Runs in browser, same run-loop Runs outside browser via driver
Language Support JavaScript/TypeScript only Multiple languages (Java, Python, etc.)
Waiting Automatic smart waiting Requires explicit waits
Debugging Time travel, snapshots, real-time Traditional debugging methods
Browser Support Chrome-family browsers All major browsers
Speed Fast (in-browser execution) Slower (network hops)
Multi-tab Not supported Fully supported
Setup Simple, zero config Complex driver management

Asynchronous Command Handling

Key Concept: Cypress commands are enqueued and executed sequentially

// Commands are automatically chained
cy.get('button').click()        // Enqueued
cy.get('.result').should('be.visible')  // Waits for previous

// No async/await needed - Cypress manages promises internally
// Each command yields a subject to the next command

API Testing Strategies

// Direct API testing
cy.request({
  method: 'POST',
  url: '/api/users',
  body: { name: 'John' },
  headers: { 'Authorization': 'Bearer token' }
}).then((response) => {
  expect(response.status).to.eq(201)
  expect(response.body).to.have.property('id')
})

// Combining API and UI testing
cy.request('POST', '/api/seed-data')  // Setup via API
cy.visit('/dashboard')                // Test UI

Authentication Patterns

// Pattern 1: UI Login (comprehensive but slow)
Cypress.Commands.add('loginViaUI', (email, password) => {
  cy.visit('/login')
  cy.get('[data-cy=email]').type(email)
  cy.get('[data-cy=password]').type(password)
  cy.get('[data-cy=submit]').click()
  cy.url().should('include', '/dashboard')
})

// Pattern 2: API Login (fast, recommended)
Cypress.Commands.add('loginViaAPI', (email, password) => {
  cy.request('POST', '/api/login', { email, password })
    .then((resp) => {
      window.localStorage.setItem('authToken', resp.body.token)
      cy.visit('/dashboard')
    })
})

// Pattern 3: Session Caching (optimal for test suites)
Cypress.Commands.add('loginWithSession', (email, password) => {
  cy.session([email, password], () => {
    cy.request('POST', '/api/login', { email, password })
      .then((resp) => {
        window.localStorage.setItem('authToken', resp.body.token)
      })
  })
})

Debugging Techniques

// Interactive debugging
cy.pause()              // Pause test execution
cy.debug()              // Debugger breakpoint

// Logging & visibility
cy.log('Custom message')         // Test runner log
cy.screenshot('debug-state')     // Capture screenshot
cy.task('log', 'Server message') // Node console log

// Command log inspection
cy.get('button').then(($btn) => {
  debugger  // Browser DevTools breakpoint
  console.log($btn)
})

Testing Patterns & Architectures

Page Object Model:

// pages/LoginPage.js
class LoginPage {
  visit() {
    cy.visit('/login')
  }
  
  fillEmail(email) {
    cy.get('[data-cy=email]').type(email)
  }
  
  submit() {
    cy.get('[data-cy=submit]').click()
  }
}

// In test
const loginPage = new LoginPage()
loginPage.visit()
loginPage.fillEmail('test@example.com')

App Actions:

// Expose app methods to Cypress
window.app = {
  login: (user) => { /* ... */ },
  resetDb: () => { /* ... */ }
}

// In test
cy.window().its('app').invoke('login', user)

Quick Reference

Command Cheat Sheet

Command Purpose
cy.visit() Navigate to URL
cy.get() Select elements
cy.contains() Find by text
cy.type() Type into input
cy.click() Click element
cy.wait() Wait for time/alias
cy.intercept() Mock network requests
cy.fixture() Load test data
cy.should() Make assertion
cy.wrap() Wrap object for Cypress commands

Assertion Cheat Sheet

Assertion Usage
exist / not.exist Element existence
be.visible / not.be.visible Visibility
have.text Exact text match
contain Partial text match
have.value Input value
have.attr Attribute value
have.class CSS class
be.checked Checkbox/radio state
be.disabled / be.enabled Element state

Common Gotchas

  1. Async handling: Cypress commands are queued, not promises
  2. Variable scope: Use aliases or .then() for values
  3. Element detachment: Re-query elements after DOM changes
  4. Cross-origin: Limited by browser security
  5. File downloads: Use cy.readFile() to verify downloads

Performance Optimization

Test Execution Speed

// Parallel execution configuration
{
  "parallel": true,
  "record": true,
  "key": "your-record-key"
}

// Optimize test structure
beforeEach(() => {
  cy.task('db:seed')  // Fast DB setup
  cy.intercept('/api/**', { fixture: 'api-responses' })  // Mock APIs
})

Memory Management

// Clear browser state between tests
afterEach(() => {
  cy.window().then(win => {
    win.sessionStorage.clear()
  })
  cy.clearCookies()
  cy.clearLocalStorage()
})

Interview Preparation Checklist

  • ✅ Core Commands: visit, get, type, click, should
  • ✅ Selectors: CSS, data attributes, best practices
  • ✅ Assertions: Chai assertions, custom matchers
  • ✅ Network: intercept, fixtures, request mocking
  • ✅ Async Handling: Command queueing, automatic waiting
  • ✅ Custom Commands: Creating reusable test helpers
  • ✅ Debugging: pause, debug, screenshots, videos
  • ✅ Best Practices: Page objects, test isolation, data attributes
  • ✅ CI/CD Integration: Headless mode, parallel execution
  • ✅ Limitations: Cross-origin, multi-tab, file uploads

Key Takeaways: Master Cypress's unique architecture (in-browser execution), understand automatic waiting and retry-ability, practice writing maintainable tests with custom commands and page objects, and know the trade-offs compared to traditional testing tools like Selenium.

Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime