cypress
End-to-end and component testing framework that runs your tests inside a real browser, alongside your app, instead of driving the browser over a remote protocol. The npm package is only a launcher: a postinstall step downloads a several-hundred-megabyte Electron-bundled binary containing the runner, an interactive GUI with time-travel debugging, network stubbing via cy.intercept, and automatic waiting that removes most explicit sleeps from tests.
Best-in-class developer experience for single-origin apps in Chrome, and the interactive runner is still unmatched. But Playwright has erased most other advantages while avoiding the browser, tab, and paid-parallelization limits, so compare hard before committing a new project.
Use it if
- You want an interactive runner with time-travel debugging that front-end developers actually enjoy using
- Your app is a single-origin web app tested mainly in Chrome-family browsers or Firefox
- You want network stubbing (cy.intercept) and automatic retry/waiting semantics instead of hand-written waits
- You want component tests for React, Vue, or Angular in the same tool as your end-to-end suite
- You need Safari/WebKit coverage: Cypress support there is experimental, while Playwright ships all three engines as first-class
- Your flows span multiple tabs: Cypress runs inside the browser and cannot control a second tab, and cross-origin steps need cy.origin ceremony
- Free CI parallelization matters: built-in parallel sharding is a paid Cypress Cloud feature, so open-source teams resort to third-party splitters
- You hate heavyweight installs: the post-install binary download is hundreds of megabytes and a recurring CI caching headache
- Your team wants plain async/await test code: Cypress commands are enqueued chains, not real promises, and that model trips up everyone at first
Setup reality
npm install cypress --save-dev then npx cypress open scaffolds config and example specs in a few minutes. The install itself is the first annoyance: a large binary lands in a system cache directory, so CI must persist that cache or every run re-downloads it. Config lives in cypress.config.js with separate e2e and component blocks. Budget real time for learning the command-queue model (no async/await), adding data-cy attributes for stable selectors, and triaging flake on animation-heavy pages.
Patterns
Visit a page and assert on contentvisit-and-assert
describe('home page', () => {
it('shows the headline', () => {
cy.visit('/');
cy.contains('h1', 'Welcome').should('be.visible');
});
});Assertions auto-retry until the default 4s timeout, so you rarely need explicit waits for rendering.
Select elements with data-cy attributesselect-by-data-attribute
// <button data-cy="submit-order">Buy</button>
cy.get('[data-cy="submit-order"]').click();CSS classes and text change with styling and copy edits; dedicated data-cy attributes are the officially recommended selector strategy.
Stub an API response with cy.interceptstub-network
cy.intercept('GET', '/api/products*', { fixture: 'products.json' }).as('getProducts');
cy.visit('/shop');
cy.wait('@getProducts');
cy.get('[data-cy="product-card"]').should('have.length', 3);Register intercepts before cy.visit or the first request can slip past the stub.
Wait for a real request and inspect itwait-for-request
cy.intercept('POST', '/api/orders').as('createOrder');
cy.get('[data-cy="submit-order"]').click();
cy.wait('@createOrder').its('request.body').should('include', { qty: 2 });Waiting on an alias replaces arbitrary cy.wait(3000) sleeps, which are the top source of slow flaky suites.
Add a reusable custom commandcustom-command
// cypress/support/commands.js
Cypress.Commands.add('getByCy', (id) => cy.get(`[data-cy="${id}"]`));
// in a spec
cy.getByCy('submit-order').click();TypeScript users must also declare the command on the Cypress.Chainable interface or specs will not compile.
Log in once and cache it across testslogin-session
Cypress.Commands.add('login', (user, pass) => {
cy.session([user, pass], () => {
cy.request('POST', '/api/login', { user, pass })
.then(({ body }) => window.localStorage.setItem('token', body.token));
});
});
beforeEach(() => cy.login('admin', 's3cret'));cy.session caches cookies and storage between tests; logging in through the UI in every test is the classic suite-speed killer.
Run headless in CIrun-headless-ci
npx cypress run --browser chrome --spec 'cypress/e2e/checkout/**'
# record video + screenshots on failure are on by default;
# cache ~/.cache/Cypress between CI runs to skip the binary downloadWithout caching the Cypress binary directory, every CI run re-downloads hundreds of megabytes.
Mount a React component testcomponent-test
import Button from './Button';
it('fires onClick', () => {
const onClick = cy.stub().as('click');
cy.mount(<Button onClick={onClick}>Save</Button>);
cy.contains('button', 'Save').click();
cy.get('@click').should('have.been.calledOnce');
});Component testing needs its own block in cypress.config.js with your bundler (vite or webpack) declared.
Pass environment config into testsenv-vars
// cypress.config.js
module.exports = {
e2e: { baseUrl: 'http://localhost:3000' },
env: { apiUrl: 'http://localhost:4000' },
};
// in a spec
cy.request(`${Cypress.env('apiUrl')}/health`);
// override at runtime: npx cypress run --env apiUrl=https://staging.example.comVariables prefixed CYPRESS_ in the shell are picked up automatically with the prefix stripped.
Upload a file in a testfile-upload
cy.get('input[type="file"]').selectFile('cypress/fixtures/invoice.pdf');
// drag and drop variant
cy.get('[data-cy="dropzone"]').selectFile('cypress/fixtures/invoice.pdf', {
action: 'drag-drop',
});selectFile is built in since Cypress 9.3; the old cypress-file-upload plugin is no longer needed.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| playwright | npm | Multi-browser including WebKit, multi-tab flows, and free parallel sharding |
| webdriverio | npm | Standards-based WebDriver automation, including mobile via Appium |
| selenium-webdriver | npm | You already run Selenium Grid infrastructure or need its language breadth |