QA Interview Hub · Knowledge base · Quizzes
Cypress
Basics and architecture
how the framework works
What is Cypress and how does it differ from Selenium?
Cypress is a framework for end-to-end and component testing of web applications in JavaScript or TypeScript.
The main difference is where the test lives. Selenium drives the browser from outside through WebDriver: every command is a separate HTTP request to the driver. Cypress runs the test inside the browser, in the same tab as the application.
Hence the advantages: direct access to the DOM and window, automatic waits, time travel through steps in the Test Runner and a built-in network interceptor. Under the hood Cypress uses Mocha for test structure (describe, it, hooks), Chai for assertions and Sinon for cy.stub, cy.spy and cy.clock.
In an interview: Cypress is a framework for e2e and component testing in JavaScript. The main difference from Selenium is in the architecture: Selenium drives the browser from outside through WebDriver, where every command is a separate HTTP request, while Cypress runs tests right in the browser, next to the application. So it has direct access to the DOM, waits for elements itself, gives time travel for debugging and can intercept the network. Inside it is built on Mocha, Chai and Sinon.
And in Playwright:Playwright also does without WebDriver, but works opposite to Cypress: it drives the browser from outside through one persistent connection and the browser's native protocol (CDP for Chromium, patched protocols for Firefox and WebKit). Tests can be written not only in JS/TS, but also in Python, Java and .NET.
How is the Cypress architecture built under the hood?
Cypress consists of two parts that constantly communicate.
- The Node process. It launches the browser, works as a proxy for all the page's HTTP traffic, runs
cy.task (files, database), and takes screenshots and video.
- The browser. The Cypress page contains two iframes: the spec iframe with your test code and the app iframe with the application. Since they are in one tab, the test sees the application's DOM directly, with no protocol in the middle.
Between the Node process and the browser there is a WebSocket. It is the proxy in Node that lets cy.intercept see and replace every request.
┌──────────── Browser (one tab) ──────────────────┐
│ spec iframe │ app iframe │
│ test + cy queue │ application │
│ direct DOM access ─────┼──► │
└──────────────┬──────────────────────────────────┘
│ WebSocket + HTTP through the proxy
┌──────────────▼──────────────────────────────────┐
│ Cypress Node process │
│ proxy (cy.intercept) · cy.task · files · video │
└─────────────────────────────────────────────────┘
In an interview: Cypress works as two processes. The first is a Node process: it launches the browser, passes all network traffic through itself as a proxy and does everything the browser can't, such as working with files or a database through cy.task. The second is the browser itself, where two iframes live in one tab: my test in one and the application in the other. That is why the test has direct access to the DOM. Between Node and the browser there is a WebSocket, and it is the proxy that lets you intercept requests with cy.intercept.
And in Playwright:Playwright has no iframe with the test next to the application. The test lives in Node, and the Playwright server sends commands to the browser through a protocol. So the test has no direct access to the DOM (only through page.evaluate), but it works without restrictions with several tabs, domains and contexts.
How does cypress open differ from cypress run?
cypress open opens the interactive Test Runner: you pick a spec, see every command, can go back to any step (time travel), and the test reruns after a file is saved. This is the mode for writing and debugging.
cypress run runs tests without a UI, headless in Electron by default, with console output and screenshots on failure. This is the mode for CI.
npx cypress open # interactive
npx cypress run # headless, Electron
npx cypress run --browser chrome # a specific browser
npx cypress run --spec "cypress/e2e/login.cy.ts"
npx cypress run --headed # with a browser window
In an interview: Open is the interactive mode for development: I see every command, can go back to any step, and the test reruns on save. Run is a headless run for CI: without a UI, with console output and screenshots on failure. In run you can specify a browser, a specific spec, or run in headed mode.
And in Playwright:The equivalent is npx playwright test, headless by default. Instead of the Test Runner there are --ui (UI mode with a timeline and DOM snapshots), --debug with the inspector, and --headed. Browsers are chosen with --project, not --browser.
What do the project structure and the Cypress config look like?
Since version 10 the config lives in the file cypress.config.js or .ts. The old cypress.json is no longer used: if you mention it as current, it betrays outdated knowledge.
A typical structure: cypress/e2e for tests, cypress/fixtures for test data, cypress/support for custom commands and global settings.
// cypress.config.ts
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'https://staging.example.com',
specPattern: 'cypress/e2e/**/*.cy.ts',
defaultCommandTimeout: 6000,
retries: { runMode: 2, openMode: 0 },
setupNodeEvents(on, config) {
// cy.task, plugins, reports
return config;
},
},
});
// cypress/
// e2e/ tests *.cy.ts
// fixtures/ JSON with test data
// support/
// e2e.ts runs before every spec
// commands.ts custom commands
In an interview: Starting from version ten the config is written in cypress.config.ts through defineConfig, with separate blocks for e2e and component testing. It sets baseUrl, the pattern for finding specs, timeouts, retries and setupNodeEvents for Node logic. Tests are in the e2e folder, test data in fixtures, and custom commands and global settings in support.
And in Playwright:The config is playwright.config.ts. Instead of separate e2e and component blocks there are projects: different browsers, devices, a setup project for login. There is no support folder: shared logic lives in fixtures through test.extend.
How do e2e tests differ from component testing in Cypress?
E2E opens the whole application through cy.visit and walks through a scenario like a user, with a real backend or network mocks.
Component testing mounts a single React, Vue or Angular component through cy.mount in a real browser, without the rest of the application. This is fast and isolated: convenient for checking states of a button, a form or a table with different props without bringing up the whole front end.
// component test
import { PriceTag } from './PriceTag';
it('shows the discount', () => {
cy.mount(<PriceTag price={100} discount={20} />);
cy.get('[data-cy=final-price]').should('have.text', '80');
});
In an interview: An E2E test opens the whole application and walks through a scenario like a user. Component testing mounts a single component with cy.mount in a real browser, without the rest of the application and without a backend. It is much faster and convenient for checking different states of a component, while e2e remains for the key end-to-end scenarios.
And in Playwright:In Playwright component testing is still experimental (@playwright/experimental-ct-react and the like). For component tests Cypress is more mature right now.
Cypress architecture and a comparison
Cypress runs tests inside the browser, in the same run loop as the application, and next to it a Node.js process works for tasks outside the browser. Selenium and Playwright drive the browser from outside: Selenium through WebDriver, Playwright through CDP and its own protocols. Because of this architecture Cypress has direct access to the DOM, window and the application's network requests.
Advantages: a convenient interactive runner with time-travel snapshots of every step, built-in retries and waits, an easy start, cy.intercept for the network, component testing. Limitations: only JavaScript/TypeScript, one tab, harder work with iframes and several domains, no Safari support (only an experimental WebKit).
Playwright is faster, has built-in parallelism without a paid service, multiple tabs and contexts, and languages besides JS. Parallelism in Cypress is possible, but usually through Cypress Cloud or third-party solutions such as sharding in CI.