Somewhere around the third year of any QA engineer's career, they get asked a Cypress interview question and answer it with a Selenium reflex. "How do you wait for this element?" gets a confident WebDriverWait and ExpectedConditions.visibilityOf(...) — a perfectly correct answer, for a tool nobody in the room is using. Cypress doesn't want that answer. It wants you to say "I don't, mostly — Cypress retries for me," and then explain why that's true, because it's the one architectural fact the entire framework is built on.
This guide covers the Cypress interview questions that actually come up — architecture versus Selenium, why .then() behaves differently than a real promise, retry-ability as the mental model underneath every command, the commands you need cold, and the flaky-test questions that separate people who've shipped a real Cypress suite from people who copied one off a tutorial. If you're prepping for a broader QA loop, pair this with our Selenium interview questions and SDET interview questions guides — this one goes deep on the parts unique to Cypress instead of repeating general test-automation ground.
Why Cypress interviews are a different animal
Selenium and Cypress solve the same problem — automate a browser — with opposite architectures, and almost every good Cypress interview question is really testing whether you understand that difference in practice, not in theory.
Selenium drives the browser from the outside. Your test process talks to a driver binary, which talks to the browser over the WebDriver protocol, issuing commands like "find this element" and "click it" as separate network round-trips. Cypress runs inside the browser, in the same run loop as your application code, injected via a Node.js server process that proxies the browser. There's no wire protocol in the middle — Cypress has direct, synchronous access to the DOM, the window object, and the network layer your app is using.
That one fact explains almost everything else an interviewer will probe:
- Why Cypress can auto-wait — it can see the DOM update in real time, in the same loop, so it can retry a command until the app is actually ready.
- Why Cypress struggled with multiple tabs and cross-origin for years — if the test runner is part of the page's world, navigating to a different origin used to mean losing that access entirely.
- Why Cypress is JavaScript/TypeScript-only — the tests execute in the browser alongside your app, so they have to speak the browser's language.
Cypress vs Selenium — the architecture questions
"Why can't Cypress open a second tab the way Selenium can?"
Because Cypress's test runner lives inside the same browser tab as your application under a shared run loop, it can't natively drive a second tab or window — there's no separate "control" process outside the page the way WebDriver has. Selenium, driving the browser externally, switches window handles freely. Cypress's official guidance is to test the behavior that opens a new tab (verify the target="_blank" link, the correct href) rather than following it — a good answer names this as a deliberate tradeoff, not a missing feature nobody thought about.
"Does Cypress support cross-origin testing?"
Yes, since Cypress 12, via cy.origin() — you wrap the block of commands that need to run on a different origin, and Cypress switches its execution context into that origin for the duration of the block. Before v12, navigating to a different superdomain inside a test would silently break Cypress's visibility into the page. This is a frequently-asked question specifically because it's a fast way to tell whether a candidate's Cypress knowledge is current or two years stale.
// logging into an SSO provider on a different origin, then back
cy.visit('https://app.example.com');
cy.origin('https://auth.example.com', () => {
cy.get('#email').type('user@example.com');
cy.get('#password').type('••••••••');
cy.get('button[type=submit]').click();
});
cy.url().should('include', '/dashboard');
"When would you actually choose Selenium over Cypress, or the reverse?"
The honest answer, and the one interviewers want: Selenium (or Playwright, which took Cypress's in-process idea and added true multi-tab, multi-origin, and multi-language support) wins when you need cross-browser coverage including Safari, multi-tab flows, or a non-JS test language to match your team's stack. Cypress wins on developer experience — the time-travel debugger, automatic screenshots and video on failure, and a test-writing loop fast enough that frontend engineers, not just QA, actually write tests in it. A candidate who says "Cypress is just better" hasn't shipped enough test suites to have hit its edges yet.
Commands, not promises — the question everyone gets wrong
This is the single most common Cypress interview trap. cy.get('.btn') looks like it returns a promise you could await, and Cypress commands even chain with .then() — but cy.get() does not return a promise. It returns a chainable command object. Cypress queues every command in a test and executes them serially, in order, after the current test function returns — not immediately, the way a promise chain would resolve.
// this test function returns before ANY of these commands have actually run
it('logs in', () => {
cy.visit('/login');
cy.get('#email').type('user@example.com');
cy.get('#password').type('secret');
cy.get('button[type=submit]').click();
cy.url().should('include', '/dashboard');
});
Nothing here is awaited, and it doesn't need to be — the Cypress command queue guarantees each line runs after the previous one settles. This is why you almost never see async/await in a Cypress test body, and why mixing it in incorrectly (say, wrapping commands in a native Promise.all) breaks the queue's ordering guarantees in confusing ways. .then() still works for reading a command's yielded value, but it's Cypress's own queued .then(), not the native Promise method — a subtlety worth stating out loud if asked, because it's the exact detail that separates "used Cypress" from "understands Cypress."
cy.get('.item').its('length').then((count) => {
expect(count).to.be.greaterThan(0);
});
Retry-ability: the core mental model
If there's one idea the entire interview keeps circling back to, it's this: Cypress commands and assertions retry automatically until they pass or a timeout is hit — you almost never write an explicit wait. cy.get() retries finding the element; .should() retries the assertion attached to it. That single mechanism is what makes Cypress tests dramatically less flaky than a naively-written Selenium suite, and it's also the thing new Cypress users fight against because they bring Selenium habits with them.
The anti-pattern interviewers are listening for you to name and reject:
// wrong: guessing at a fixed delay
cy.get('.save-btn').click();
cy.wait(2000);
cy.get('.toast').should('be.visible');
// right: let the assertion retry until it's true, or times out with a clear error
cy.get('.save-btn').click();
cy.get('.toast').should('be.visible').and('contain', 'Saved');
cy.wait(2000) is either too short (still flaky on a slow CI runner) or too long (a slow, wasteful suite) — it's guessing at a number instead of waiting for a fact. The one legitimate use of cy.wait() is waiting on a named network alias (cy.wait('@getUser')), which waits for a specific request to complete rather than for arbitrary time to pass — that's a fact, not a guess, and interviewers consider it the correct pattern.
.then() chaining quirk, why cy.wait(ms) is a smell — is the same architectural fact wearing a different costume: Cypress runs in-browser with a queued command model and built-in retry-ability, and Selenium doesn't.The commands you need cold
cy.get(selector)— the core query command; retries until the element exists (and, combined with.should(), until it satisfies the assertion). Preferdata-cy/data-testidattributes over CSS classes so tests don't break when a designer renames a class.cy.contains(text)— finds an element by its visible text, often combined withcy.get()to scope the search (cy.get('.card').contains('Upgrade')).cy.intercept()— stubs or spies on network requests. Replaced the oldercy.route()(Cypress 5+) and is usually the most-discussed command in a mid-to-senior interview, because it's how you make tests deterministic instead of dependent on a real, slow, flaky backend.
cy.intercept('GET', '/api/users/*', { fixture: 'user.json' }).as('getUser');
cy.visit('/profile');
cy.wait('@getUser').its('response.statusCode').should('eq', 200);
cy.get('.username').should('contain', 'Priya');
- Custom commands (
Cypress.Commands.add) — how a real suite avoids repeating a five-line login flow in every test file. A candidate who's built a real framework has acy.login()custom command; a candidate who's only followed tutorials usually hasn't needed one yet.
// cypress/support/commands.js
Cypress.Commands.add('login', (email, password) => {
cy.request('POST', '/api/login', { email, password }).then((res) => {
window.localStorage.setItem('authToken', res.body.token);
});
});
// in a test:
cy.login('user@example.com', 'secret');
Note the pattern: logging in via cy.request() (a real API call, no UI) instead of clicking through a login form in every single test — a strong interview answer, because it shows you think about suite runtime, not just individual test correctness.
cy.fixture()— loads static JSON test data, most often paired withcy.intercept()to stub a response.- Aliases (
.as()) — name an element, request, or value so you can reference it later in the test (cy.wait('@getUser'),cy.get('@savedUser')), which also makes long test files far more readable than nestedcy.get()chains.
Flaky-test questions
Every Cypress interview eventually asks some version of "tell me about a flaky test you fixed." Good answers name a real mechanism, not "I added a wait":
- Fixed-time waits (
cy.wait(1000)) — replace with retrying assertions or a network alias. - Testing against a real, shared backend — different runs see different data; stub the network with
cy.intercept()so the test's inputs are fixed. - Animation and transition timing — Cypress's
should('be.visible')can pass mid-animation on an element that's about to move; disabling CSS animations in the test environment is a common, honest fix to name. - Test order dependence — a test that only passes because an earlier test left the database in a particular state. The fix is state isolation: seed data per test (often via
cy.request()against a test API) rather than depending on suite order. - Over-broad selectors —
cy.get('button')matching more than one element as the app grows;data-cyattributes exist specifically to keep selectors immune to markup and copy changes.
How this fits an SDET interview loop
A dedicated Cypress round rarely stands alone — it's usually one segment inside a broader SDET interview loop: a coding round (often plain JavaScript, not Cypress-specific), a test-design discussion, a framework-design conversation where Cypress specifics like the ones above come up, and a behavioral round about a bug that escaped to production. If the job description says "Selenium or Cypress," expect the interviewer to let you pick — and expect them to probe whichever one you didn't pick, briefly, to check you understand why teams choose one over the other rather than only knowing the one you happened to use.
How people actually prepare for this — and where it falls short
Most Cypress prep is reading the official docs (genuinely good — docs.cypress.io is one of the better-written framework docs out there) or scanning a GeeksforGeeks-style question dump the night before. Both get you the vocabulary. Neither rehearses the part that actually loses candidates points: explaining why cy.wait(2000) is wrong, out loud, to someone who then asks "okay, but what if the network is slow that day?" — a live follow-up a static answer sheet can't produce. Pasting the same question into ChatGPT gets you a correct explanation to read silently, which is a different skill from producing one verbally, under a little pressure, with a stranger listening for whether you actually understand the retry model or just memorized the term.
Greenroom, built around Ari, our AI interviewer, runs spoken technical mock interviews that ask real follow-ups — including the "what if the network is slow" kind — and gives feedback on how clearly you explained the mechanism, not just whether your final answer was technically correct. It won't fabricate a "% of Cypress interviews we've helped pass" statistic, because we don't have one yet; what it does is make you say the retry-ability answer out loud before a real interviewer asks it first. The honest tradeoff: Greenroom doesn't run or grade your actual Cypress test suite the way a real take-home would — it's a spoken concept check, not a coding sandbox, so still write and run real Cypress specs against a real app as the other half of your prep.
Frequently asked questions
What Cypress interview questions come up most often?
The architecture difference from Selenium (in-browser vs WebDriver), why commands aren't promises and how .then() behaves differently, retry-ability and why cy.wait(ms) is an anti-pattern, cy.intercept() for network stubbing, and how to debug a flaky test are the five that come up in nearly every Cypress round, from junior QA screens to senior SDET loops.
Is Cypress or Selenium more commonly asked about in interviews?
It depends on the company's actual stack, which is exactly what interviewers are testing for — a candidate who knows both and can explain the tradeoff is stronger than one who only knows the tool the job description happens to mention. Older enterprises and companies needing broad cross-browser or multi-language coverage lean Selenium; product companies and frontend-heavy teams that value developer experience lean Cypress. See our Selenium interview questions guide for the WebDriver side.
Does Cypress support testing multiple tabs or windows?
Not natively — Cypress runs inside a single browser tab in the same run loop as your application, so it can't drive a second tab the way Selenium switches window handles. The standard approach is to test that a new-tab link has the correct target and href rather than following it, or to use cy.origin() for same-tab cross-origin navigation, which Cypress has supported since version 12.
Why doesn't Cypress use explicit waits like Selenium?
Because Cypress commands and assertions retry automatically until they pass or a timeout elapses, since Cypress has direct, in-process access to the DOM and can see it update in real time. Selenium drives the browser externally over the WebDriver protocol and has no equivalent built-in visibility, which is why Selenium tests typically need explicit WebDriverWait calls that Cypress tests don't.
What is cy.intercept() used for?
cy.intercept() stubs, spies on, or modifies network requests during a test, letting you control exactly what the frontend receives without hitting a real backend — making tests deterministic and fast. It replaced the older cy.route() command starting in Cypress 5, and is usually the most-discussed single command in a mid-to-senior Cypress interview because it's central to writing reliable, non-flaky suites.
How do I explain a flaky test I fixed in a Cypress interview?
Name the actual mechanism, not "I added a wait": a fixed-time cy.wait(ms) replaced with a retrying assertion or network alias, a shared/real backend replaced with cy.intercept() stubs, a test-order dependency fixed with per-test data seeding, or an over-broad CSS selector replaced with a dedicated data-cy attribute. Interviewers are listening for a specific, correct diagnosis, not a vague "I made it more stable."