Cypress

On this page 7

Cypress is an open-source JavaScript test framework for end-to-end and component testing of web applications, in which test code runs inside the same browser as the application under test rather than controlling it from outside. In web testing it is widely used by frontend teams to simulate user journeys such as signing in, filling forms and completing purchases, and to verify the interface automatically on every code change.

Its distinctive architecture gives it automatic waiting, time-travel debugging and direct network control, which make tests easier to write and debug. The same design also brings limits, notably around multiple tabs, multiple origins and browser coverage, that separate it from neighbors such as Playwright and Selenium.

How Cypress runs tests inside the browser

Cypress starts a Node.js server process and launches a browser in which the application loads in an iframe while the test runner occupies the surrounding page. Test commands execute in the browser’s JavaScript context, so they can access the DOM, the window object and application code directly. The Node.js process handles tasks the browser cannot, such as file system access, database seeding and network proxying.

Commands such as cy.get(), cy.click() and cy.contains() do not run immediately. They are enqueued and executed in order, and each query is retried until its assertion passes or a timeout expires, by default 4 seconds. This built-in retry-ability removes most manual sleeps and is the main reason Cypress tests are less prone to timing failures than naive scripts.

ElementExampleRole
Queriescy.get, cy.contains, cy.findLocate elements, retried automatically
Actionscy.click, cy.type, cy.selectSimulate user interaction
Assertionsshould, expectVerify state, retried with the preceding query
Network controlcy.interceptObserve, stub or delay HTTP requests
Node taskscy.taskRun server-side code such as database resets
Custom commandsCypress.Commands.addReuse steps such as logging in
Main building blocks of a Cypress test

End-to-end and component testing modes

Cypress offers two testing types. End-to-end testing loads the full application from a URL and exercises it as a user would. Component testing mounts a single UI component in isolation using the project’s bundler, which is faster and lets developers test states that are hard to reach through the full interface.

AspectEnd-to-end testingComponent testing
ScopeWhole application with backendOne component with mocked props and data
SpeedSeconds per testTypically well under a second per test
SetupRunning application and test dataFramework adapter and bundler configuration
Best forCritical user journeysVisual states, edge cases, design system parts
Cypress testing types compared

A balanced suite follows the testing pyramid: many fast unit tests, a solid layer of component tests and a smaller set of end-to-end tests covering the journeys that generate revenue or risk. Running hundreds of end-to-end tests for every detail usually produces slow, fragile pipelines.

Cypress compared with Playwright and Selenium

Cypress, Playwright and Selenium automate browsers in fundamentally different ways. Selenium drives browsers from outside through the WebDriver protocol, Playwright controls them through low-level automation protocols from a separate process, and Cypress runs inside the page. These architectures explain most differences in capability.

PropertyCypressPlaywrightSelenium
ArchitectureRuns in the browserControls browser from outsideWebDriver protocol
LanguagesJavaScript and TypeScriptJavaScript, Python, Java, .NETMost major languages
BrowsersChromium family, Firefox, WebKit experimentalChromium, Firefox, WebKitAll major browsers
Multiple tabsNot supportedSupportedSupported
Automatic waitingYes, retry-basedYes, auto-wait on actionsExplicit waits required
DebuggingInteractive runner with snapshotsTrace viewerDepends on tooling
Browser automation tools for web testing

Cypress is often the easiest start for frontend developers because of its interactive runner. Playwright tends to win when tests need multiple tabs, several origins or parallel runs without a paid service. Selenium remains relevant for broad language and browser grid coverage.

Writing reliable Cypress tests

Reliable tests depend on stable selectors, controlled data and independent tests. Selecting elements by CSS classes or text that designers change frequently is the most common source of breakage; dedicated attributes such as data-cy or accessible roles and labels are far more stable.

  • Reset state before each test with cy.task or API calls instead of depending on the previous test.
  • Log in programmatically through an API request and cy.session rather than through the login form in every test.
  • Stub unstable third-party requests with cy.intercept while keeping at least one test against the real backend.
  • Avoid fixed waits such as cy.wait(3000); wait for an aliased request or an assertion instead.
  • Keep tests short and focused on one behavior so a failure points to a clear cause.

Limitations and common mistakes

The in-browser architecture brings real constraints. Tests cannot open or switch between tabs, and visiting several origins in one test requires the cy.origin command. Native browser dialogs, file downloads and hover-only menus need workarounds, because Cypress dispatches synthetic events by default rather than operating-system input.

ProblemTypical causeRemedy
Flaky testsShared state or uncontrolled network timingIsolate data and alias requests
Slow suiteToo many UI logins and long journeysUse cy.session and component tests
Broken selectorsStyling classes used as locatorsUse data attributes or roles
False passesAssertions on stubbed data onlyKeep a contract or real-backend test
Async confusionTreating commands as promisesChain commands and use then callbacks
Frequent problems in Cypress suites

A common conceptual mistake is assigning a command result to a variable as if it were synchronous. Cypress commands return chainable objects, not values, so data must be passed through .then() or aliases.

Explained without jargon

Cypress is a tool that acts like a tireless tester. It opens a website in a browser and clicks buttons. It types into forms and checks that the right things appear on the screen.

Developers write down the steps once, for example: log in, add a product to the cart, pay. Cypress then repeats those steps after every change to the website. If something breaks, it shows a recording of each step. That makes it easy to see what went wrong.

A typical use case

A software company sells a subscription web app, and a release once broke the upgrade-plan button for users with expired cards without anyone noticing for two days. The team decides to protect its critical journeys with automated tests.

They write 25 Cypress end-to-end tests covering sign-up, login, plan upgrade, billing updates and cancellation, plus about 150 component tests for form fields and pricing tables. Test users are created through an API task before each test, login uses cy.session, and the payment provider is stubbed with cy.intercept except in one nightly test that uses its sandbox environment.

The suite runs in the continuous integration pipeline in about eight minutes on every pull request. Within the first quarter it blocks three releases that would have broken billing flows, and the team no longer relies on manual checks before each deployment.