📚 4 min read
Cypress Guide
Cypress is a next-generation front-end testing tool built for the modern web. It enables fast, easy and reliable testing for anything that runs in a browser.
Key Features
- Real-time reloads
- Automatic waiting
- Time travel debugging
- Network traffic control
- Screenshots and videos
- Cross-browser testing
- Interactive test runner
- Flake detection
Getting Started
bash
# Install Cypress
npm install --save-dev cypress
Basic Test Structure
javascript
describe('Login Page', () => {
beforeEach(() => {
cy.visit('/login');
});
it('successfully logs in with valid credentials', () => {
cy.get('[data-cy="username"]').type('testuser');
cy.get('[data-cy="password"]').type('password123');
cy.get('[data-cy="submit"]').click();
cy.url().should('include', '/dashboard');
cy.get('[data-cy="welcome-message"]').should(
'contain',
'Welcome, Test User'
);
});
});
Common Commands
javascript
// Navigation
cy.visit('/about');
cy.go('back');
cy.reload();
// Interacting with elements
cy.get('.button').click();
cy.get('input').type('Hello');
cy.get('select').select('option1');
// Assertions
cy.get('.title').should('exist');
cy.get('.count').should('have.text', '5');
cy.get('.disabled').should('be.disabled');
// Network requests
cy.intercept('GET', '/api/users', { fixture: 'users.json' });
cy.request('POST', '/api/data', { name: 'test' });
// Custom commands
Cypress.Commands.add('login', (email, password) => {
cy.get('[data-cy="email"]').type(email);
cy.get('[data-cy="password"]').type(password);
cy.get('[data-cy="submit"]').click();
});
Advanced Features
Custom Commands
javascript
// cypress/support/commands.js
Cypress.Commands.add('login', (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();
});
// Usage in tests
cy.login('user@example.com', 'password123');
Fixtures
javascript
// cypress/fixtures/user.json
{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
// Usage in tests
cy.fixture('user').then((user) => {
cy.get('[data-cy="name"]').type(user.name)
cy.get('[data-cy="email"]').type(user.email)
})
Network Interception
javascript
// Mock API response
cy.intercept('GET', '/api/users', {
statusCode: 200,
body: { users: [] },
}).as('getUsers');
// Wait for request
cy.wait('@getUsers');
// Modify response
cy.intercept('GET', '/api/users', (req) => {
req.reply((res) => {
res.body.users = [...res.body.users, { id: 999, name: 'Test User' }];
return res;
});
});
// Force error response
cy.intercept('POST', '/api/users', {
statusCode: 500,
body: { error: 'Server error' },
});
Testing Patterns
Page Objects
javascript
// cypress/support/pages/login.page.js
class LoginPage {
visit() {
cy.visit('/login');
}
getEmailInput() {
return cy.get('[data-cy="email"]');
}
getPasswordInput() {
return cy.get('[data-cy="password"]');
}
getSubmitButton() {
return cy.get('[data-cy="submit"]');
}
login(email, password) {
this.getEmailInput().type(email);
this.getPasswordInput().type(password);
this.getSubmitButton().click();
}
}
export default new LoginPage();
// Usage in tests
import LoginPage from '../support/pages/login.page';
it('should login successfully', () => {
LoginPage.visit();
LoginPage.login('user@example.com', 'password123');
cy.url().should('include', '/dashboard');
});
API Testing
javascript
describe('API Tests', () => {
it('creates a new user', () => {
cy.request('POST', '/api/users', {
name: 'John Doe',
email: 'john@example.com',
}).then((response) => {
expect(response.status).to.eq(201);
expect(response.body).to.have.property('id');
});
});
it('handles errors correctly', () => {
cy.request({
method: 'POST',
url: '/api/users',
body: { name: '' },
failOnStatusCode: false,
}).then((response) => {
expect(response.status).to.eq(400);
expect(response.body).to.have.property('error');
});
});
});
Configuration
cypress.config.js
javascript
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
video: false,
screenshotOnRunFailure: true,
defaultCommandTimeout: 5000,
requestTimeout: 10000,
responseTimeout: 30000,
retries: {
runMode: 2,
openMode: 0,
},
},
env: {
apiUrl: 'http://localhost:3001',
coverage: false,
},
});
Environment Variables
javascript
// cypress.env.json
{
"auth_token": "secret-token",
"api_key": "12345"
}
// Usage in tests
cy.visit(`${Cypress.env('apiUrl')}/users`)
Best Practices
1. Selectors
javascript
// ❌ Avoid
cy.get('.submit-button');
cy.get('#login-form');
cy.get('button').contains('Submit');
// ✅ Prefer
cy.get('[data-cy="submit-button"]');
cy.get('[data-cy="login-form"]');
cy.get('[data-cy="submit"]');
2. Waiting
javascript
// ❌ Avoid
cy.wait(5000);
// ✅ Prefer
cy.get('[data-cy="element"]').should('be.visible');
cy.get('[data-cy="element"]').should('exist');
cy.intercept('/api/data').as('getData');
cy.wait('@getData');
3. Assertions
javascript
// Element state
cy.get('[data-cy="button"]')
.should('be.visible')
.and('not.be.disabled')
.and('have.text', 'Submit');
// Multiple assertions
cy.get('[data-cy="user"]').should(($el) => {
expect($el).to.have.length(3);
expect($el.first()).to.contain('John');
expect($el.last()).to.contain('Jane');
});
Testing Strategies
Visual Testing
javascript
describe('Visual Tests', () => {
it('matches homepage screenshot', () => {
cy.visit('/');
cy.matchImageSnapshot('homepage');
});
it('matches mobile layout', () => {
cy.viewport('iphone-x');
cy.visit('/');
cy.matchImageSnapshot('homepage-mobile');
});
});
Component Testing
javascript
import Button from './Button';
describe('Button Component', () => {
it('renders correctly', () => {
cy.mount(<Button>Click me</Button>);
cy.get('button').should('have.text', 'Click me');
});
it('handles click events', () => {
const onClick = cy.stub().as('onClick');
cy.mount(<Button onClick={onClick}>Click me</Button>);
cy.get('button').click();
cy.get('@onClick').should('have.been.called');
});
});
Performance Testing
Measuring Performance
javascript
cy.window().then((win) => {
const performance = win.performance;
const navigation = performance.getEntriesByType('navigation')[0];
expect(navigation.domContentLoadedEventEnd).to.be.lessThan(2000);
expect(navigation.loadEventEnd).to.be.lessThan(5000);
});
Resource Loading
javascript
cy.window().then((win) => {
const resources = win.performance.getEntriesByType('resource');
// Check image sizes
const images = resources.filter((r) => r.initiatorType === 'img');
images.forEach((img) => {
expect(img.transferSize).to.be.lessThan(500000);
});
// Check total page weight
const totalSize = resources.reduce((sum, r) => sum + r.transferSize, 0);
expect(totalSize).to.be.lessThan(5000000);
});
Debugging
Console Output
javascript
cy.get('[data-cy="element"]').then(($el) => {
console.log('Element:', $el);
});
// Debug command
cy.debug();
// Pause execution
cy.pause();
Screenshots and Videos
javascript
// Take screenshot
cy.screenshot('error-state');
// Screenshot specific element
cy.get('[data-cy="modal"]').screenshot('modal');
// Configure video recording
module.exports = defineConfig({
e2e: {
video: true,
videoCompression: 32,
videosFolder: 'cypress/videos',
},
});