Architecture • Retry-ability • cy.intercept • Component Tests • CI and Flakes • 2026

Cypress Interview Questions

30 questions What each one tests, an answer frame, a spoken answer 36 min read

This page is for testers and developers facing a Cypress round, from a first automation job to a senior role that owns the suite. Most rounds start with how Cypress runs inside the browser and why its commands are queued, then test selectors, retry-ability and waits, network stubbing with cy.intercept, fixtures and test data, and custom commands. Stronger rounds add component tests, CI and parallel runs, flaky tests and a fair comparison with other tools. Each question shows what the interviewer is checking, the shape of a good answer and a short answer you can say out loud.

Search all questions by round, difficulty and level, or save the ones you want to practise.

Architecture 6 questions

Medium Technical round Fresher, Mid-level Practice question

1. Cypress is often described as running inside the browser. What does that mean, and how is it different from a WebDriver-based tool?

What the interviewer is really testing:
Whether you understand the architecture well enough to explain both its strengths and its known limits, instead of reciting a feature list.
Answer frame:

In the browser: your spec runs in the same browser as the app, in its own frame, so it can reach the DOM, window and storage directly.

Node side: a Node process runs alongside for what a browser can't do: files, tasks, and proxying network traffic.

Contrast: a WebDriver tool runs the test outside the browser and sends each command through a driver.

Trade-off: built-in waiting and great debugging, but JavaScript or TypeScript only and one tab at a time.

Sample spoken answer:

"When I run a Cypress test, my spec is bundled and loaded into the browser itself, in a frame next to the application under test. So the test runs in the same run loop as the app and can touch the DOM, the window object and local storage directly. Behind that there's a Node process that handles what a browser sandbox can't: reading files, running tasks like seeding a database, and sitting in the middle of network traffic, which is how intercepting requests works. A WebDriver tool is different. The test runs in a separate process and sends each command to a driver, which tells the browser what to do. Being inside gives Cypress automatic waiting, fast feedback and the snapshot debugging in the runner. The cost is that tests are JavaScript or TypeScript only, and things like controlling a second tab are outside what it's built for."

Red flag to avoid:

Saying Cypress is built on Selenium or WebDriver, or not knowing there is a Node process working behind the browser.

They may ask next:
  • Why can a Cypress test call a function on the app's window object, and when would you actually do that?
  • Which kinds of work have to go through the Node side instead of the browser?
Say it in 60 seconds
Medium Coding round Fresher, Mid-level Practice question

2. Why can't you write const title = cy.get('h1') and then use title like an element? How do you get the text out to use later?

What the interviewer is really testing:
Whether you understand that Cypress commands are queued and run later, which is the root of most beginner bugs in Cypress code.
Answer frame:

Queued: each cy command is added to a queue and runs later, in order; it returns a chainable, not the element.

Not a promise: you can't await it or read its result in the next plain line of code.

Getting values: use .invoke() and .then() to yield the value, or save it with .as() and read it back.

Sample spoken answer:

"Cypress commands don't run when the line executes. Calling cy.get just adds a step to a queue and returns a chainable object, and the queue runs after the test function has finished registering everything. So title isn't an element or a string, and if I log it I get the chainable, not the heading. It also isn't a real promise, so await won't do what people expect. To use the value, I stay inside the chain. I call invoke with text, then use then to get the actual string in a callback, and anything that depends on it goes inside that callback. If I need it in a later step, I save it with as and read it back with cy.get and the alias name, or with this and the alias inside a normal function, not an arrow function."

Code:
cy.get('h1')
  .invoke('text')
  .then((title) => {
    cy.get('[data-cy=breadcrumb]').should('contain', title.trim())
  })

cy.get('[data-cy=order-id]').invoke('text').as('orderId')
cy.get('@orderId').then((id) => {
  cy.visit('/orders/' + id.trim())
})
Red flag to avoid:

Using async and await on cy commands, or assigning the result of cy.get to a variable and treating it as the element.

They may ask next:
  • Why does this.orderId come back undefined inside an arrow function?
  • What happens to the value you return from a .then callback, and when does that matter?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

3. Your tests pass when the whole spec runs, but one fails when you run it alone with it.only. What's going on, and how do you fix it?

What the interviewer is really testing:
Whether you know tests must stand on their own, and what Cypress does and doesn't reset between them.
Answer frame:

Cause: the test leans on state left by an earlier one: a login, created data, or the page it ended on.

What resets: with test isolation on, Cypress clears the page, cookies, local storage and session storage before each test.

Fix: each test sets up what it needs in beforeEach, through API calls or cy.session, never through an earlier test.

Sample spoken answer:

"That's the classic sign of tests depending on each other. Say the first test logs in and creates a product, and the second one edits that product. Run together, it works. Run the second one alone and there's nothing to edit. Newer Cypress versions actually push against this: with test isolation on, it clears the page, cookies, local storage and session storage before every test, so leaning on the previous test's browser state fails fast. The fix is to make each test stand on its own. I move shared setup into a beforeEach, create the data through an API call or a task instead of through the UI, and cache the login with cy.session so it stays quick. Then any test can run alone, in any order, and I can split specs across machines later without surprises."

Red flag to avoid:

Fixing it by forcing test order or by merging the tests into one long test.

They may ask next:
  • What state does Cypress not reset between tests that could still leak from one test to the next?
  • Why is turning test isolation off a poor fix for this?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

4. The app opens a help link in a new tab, and login redirects to a third-party sign-in page. How do you test both in Cypress?

What the interviewer is really testing:
Whether you know the tool's real limits and can work around them without testing someone else's product in every run.
Answer frame:

New tab: Cypress drives one tab, so check the link's href and target, or remove the target and follow it in the same tab.

Other origin: wrap commands for the second site in cy.origin, used sparingly.

Everywhere else: log in programmatically and cache it with cy.session.

Principle: test your app's side of the handshake, not the provider's page.

Sample spoken answer:

"Both run into how Cypress works. It controls one tab, so for the help link I don't try to switch windows. I check the link has the right href and target, and if I need to see the page it opens, I remove the target attribute and click it so it loads in the same tab, or I just cy.request the URL and check it responds. For the third-party sign-in, Cypress has cy.origin, which lets me run commands against a second origin inside a callback, so a real login through that page is possible. But I'd do that in one or two tests only. Everywhere else I'd log in programmatically, for example by getting a token through the identity provider's API or a test endpoint, and cache it with cy.session. Clicking through someone else's page in every test is slow, and when they change it or rate-limit it, my whole suite goes red."

Code:
cy.get('[data-cy=help-link]')
  .should('have.attr', 'target', '_blank')
  .invoke('removeAttr', 'target')
  .click()
cy.url().should('include', '/help')
Red flag to avoid:

Claiming Cypress switches between tabs like a WebDriver tool, or making every test click through the third-party login page.

They may ask next:
  • Why can't a cy.origin callback use variables from the surrounding test directly?
  • How would you check the redirect back from the sign-in page carries the right state?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level, Senior Practice question

5. How does Cypress compare with Playwright and Selenium, and when would you pick Cypress?

What the interviewer is really testing:
Whether you can compare tools fairly on architecture and real needs instead of repeating hype for one of them.
Answer frame:

Cypress: in the browser; JavaScript or TypeScript; strong debugging, retries, stubbing and component tests; one tab.

Playwright: drives browsers from outside; several languages; multiple tabs and contexts; Chromium, Firefox and WebKit.

Selenium: the WebDriver standard; widest language and browser reach, including real Safari; more to build yourself.

Pick Cypress: a JavaScript front-end team, single-tab flows, Chrome-family and Firefox coverage.

Sample spoken answer:

"They solve the same problem in different ways. Cypress runs inside the browser, which gives it very good debugging with the time-travel runner, automatic retries, easy network stubbing and component testing, but it's JavaScript or TypeScript only and it drives one tab at a time. Playwright drives browsers from outside through their automation protocols, supports several languages, handles multiple tabs and isolated browser contexts, covers WebKit, and runs tests in parallel on one machine out of the box. Selenium is the WebDriver standard: the widest language and browser reach, including real Safari and large grids, but you assemble more of the framework yourself. I'd pick Cypress for a front-end team working in JavaScript that wants quick, readable tests and component tests, where the flows stay in one tab and Chrome-family and Firefox cover the users. If real Safari or multi-tab flows are central, I'd look elsewhere."

Red flag to avoid:

Declaring one tool best for everything, or repeating old limits without checking what current versions support.

They may ask next:
  • What would make you move an existing Cypress suite to another tool, and what would make you stay?
  • How would you run a fair trial between two tools before deciding?
Say it in 60 seconds
Medium Situational round Mid-level, Senior Practice question

6. A new product's users are mostly on iPhones and Macs, and checkout opens the payment step in a new window. The team wants Cypress. What's your advice?

What the interviewer is really testing:
Whether you judge a tool against the product's real requirements and propose a way to decide, rather than going on habit.
Answer frame:

Map needs to limits: real Safari coverage and multi-window flows are where Cypress is weakest.

Options: Cypress for most tests plus another route for Safari and the payment step, or a different tool.

Decide with a spike: automate the hardest flow before committing.

Be honest: never report Safari coverage from Chrome-only runs.

Sample spoken answer:

"I'd respect the team's instinct if they know Cypress well, but these two requirements hit its weak spots. Cypress mainly covers Chrome-family browsers and Firefox, and its WebKit option has been experimental, which still isn't the same as real Safari on a real device, and it drives one tab, so a payment step in a new window needs a workaround. I wouldn't settle it in a meeting. I'd run a short spike: automate checkout in Cypress, stubbing or bypassing the payment window, and in one alternative that handles WebKit and multiple pages, then compare how honest, stable and quick each is. A mixed answer is often fine: Cypress for component tests and most flows, which developers will happily write, plus a small set of checks on real Safari, through another tool or a device cloud, for the journeys iPhone users depend on. What I wouldn't do is claim Safari coverage from Chrome-only runs."

Red flag to avoid:

Picking or rejecting Cypress by habit without checking it against the Safari and new-window requirements.

They may ask next:
  • How would you test the payment step without driving the payment provider's page?
  • How do you justify the cost of a second tool to the team?
Say it in 60 seconds

Selectors 2 questions

Easy Technical round Fresher, Mid-level Practice question

7. How do you choose selectors in Cypress so tests don't break every time someone restyles a page?

What the interviewer is really testing:
Whether you pick selectors that survive design changes, which decides how much time the team spends fixing tests that aren't really broken.
Answer frame:

Best: dedicated test attributes like data-cy or data-test, agreed with developers.

Good: visible text through cy.contains when the text is what the user relies on.

Avoid: generated class names, long CSS paths, index-based picks and ids that change per build.

Sample spoken answer:

"I ask developers to add dedicated test attributes, like data-cy equals submit-order, to the elements we test. Those attributes exist only for tests, so nobody changes them when restyling, and anyone who touches one can see a test depends on it. Where the visible text is the point, like a button labelled Place order, I use cy.contains, because if that text changes the test probably should notice. What I avoid is anything tied to styling or structure: generated class names, long paths like a div inside a div inside the third span, or eq with an index that shifts when a new row appears. The selector playground in the Cypress runner suggests selectors and prefers data attributes too, so it's a fair starting point, but I still check that what it picks makes sense."

Red flag to avoid:

Relying on long CSS paths or generated class names and then calling the resulting breakage flakiness.

They may ask next:
  • The developers won't add test attributes. What's your next best option?
  • When is matching on visible text a bad idea, for example in an app with several languages?
Say it in 60 seconds
Easy Coding round Fresher, Mid-level Practice question

8. What's the difference between cy.get, .find and cy.contains? How would you click Delete on the row for one specific user?

What the interviewer is really testing:
Whether you can scope a search to the right part of the page instead of relying on position.
Answer frame:

cy.get: searches from the document root, even when chained after another command.

.find: searches only inside the previous subject.

cy.contains: matches by text, and a selector argument says which element should hold that text.

Scoping: find the row by something unique, then use .find() or .within() inside it.

Sample spoken answer:

"cy.get always searches from the root of the document, even if I chain it after another command, which surprises people. The one exception is inside a within block. find searches only inside whatever the previous command yielded. contains matches on text, and I can give it a selector too, so contains with tr and an email address gives me the table row that holds that email, not just the cell. To click Delete for one user, I find the row by the email, then search inside it, either with find or with within, which scopes every command in its callback to that row. That way, if the table gets sorted or a new user appears above, the test still clicks the right button, because it never relied on a row number."

Code:
cy.contains('tr', 'sam@example.com')
  .find('[data-cy=delete-user]')
  .click()

// same thing, scoped with within
cy.contains('tr', 'sam@example.com').within(() => {
  cy.get('[data-cy=delete-user]').click()
})
Red flag to avoid:

Clicking by row index like eq(3), so the test breaks as soon as the data order changes.

They may ask next:
  • What does cy.contains yield when the text appears in several elements?
  • Inside a .then callback on the row, would cy.get still be limited to that row?
Say it in 60 seconds

Retries & Waits 4 questions

Medium Technical round Fresher, Mid-level, Senior Practice question

9. What does retry-ability mean in Cypress? Which commands retry, and which ones run only once?

What the interviewer is really testing:
Whether you understand the core idea that makes Cypress tests stable without sleeps, and where it stops helping.
Answer frame:

Queries retry: cy.get, .find, .contains and similar re-run until the assertions after them pass or time out.

Assertions drive it: .should keeps the query before it retrying against a fresh page.

Actions don't retry: .click and .type run once, after waiting for the element to be actionable.

Pattern: query, assert the state, then act; no checks hidden in .then.

Sample spoken answer:

"Retry-ability is how Cypress avoids most explicit waits. Queries like get, find and contains don't just look once. If there's an assertion after them, Cypress keeps re-running the query and re-checking the assertion until it passes or the command timeout runs out, and even without an explicit should, a query retries until the element exists. In recent versions the chain of queries is re-run together, so if the page re-renders, it picks up the fresh element. Actions like click and type are different. They don't repeat, because clicking twice could have side effects, but before acting they wait until the element is visible, enabled and not covered. So my pattern is: query, assert it's in the state I expect, then act. And I don't put assertions inside a plain then callback, because then runs once and doesn't retry."

Red flag to avoid:

Saying every Cypress command retries, or adding waits to fix timing that retry-ability already handles.

They may ask next:
  • Why doesn't Cypress retry a click that seemed to do nothing?
  • How do you give one slow query more time without raising the timeout for the whole suite?
Say it in 60 seconds
Easy Coding round Fresher, Mid-level Practice question

10. A teammate's tests are full of cy.wait(3000). Why is that a problem, and what would you replace it with?

What the interviewer is really testing:
Whether you wait on real signals, the page state or the network, rather than guessing a number of seconds.
Answer frame:

Problem: a fixed wait is too long on a fast run and too short on a slow one, so the suite is slow and still flaky.

Wait on state: assert on what the user would see and let retry-ability do the waiting.

Wait on network: alias the request with cy.intercept and wait on the alias.

Sample spoken answer:

"A fixed wait is a guess. On a fast run it wastes three seconds every time, and across hundreds of tests that adds up. On a slow CI machine three seconds isn't enough, and the test fails anyway, so it's slow and still flaky. Most of the time I don't need a wait at all, just an assertion on the thing I'm waiting for, like the spinner should not exist, or the table should contain the new row, and Cypress retries until that's true. When the test really depends on a request finishing, I set up cy.intercept for that call before the action, give it an alias and then wait on the alias. That waits exactly as long as the request takes, and I can check the status code as well, so a failure tells me the API broke rather than just timing out."

Code:
cy.intercept('POST', '/api/orders').as('createOrder')
cy.get('[data-cy=place-order]').click()
cy.wait('@createOrder').its('response.statusCode').should('eq', 201)
cy.contains('[data-cy=toast]', 'Order placed').should('be.visible')
Red flag to avoid:

Raising the fixed wait until the test goes green.

They may ask next:
  • Why must the intercept be registered before the click and not after it?
  • Is there ever a good reason to call cy.wait with a number?
Say it in 60 seconds
Medium Coding round Mid-level, Senior Practice question

11. What's the difference between .should with a callback and .then? Show me a check that needs a callback.

What the interviewer is really testing:
Whether you know which of the two retries, and can write a custom check that stays stable while the page is still loading.
Answer frame:

.should(cb): re-runs the callback and the query before it until nothing throws or the timeout hits.

.then(cb): runs once with whatever was yielded; no retry.

Rule: .should for checks on a changing page, .then for using a settled value; keep .should callbacks free of cy commands and side effects.

Sample spoken answer:

"Both take a callback, but they behave very differently. With should, Cypress keeps calling my callback, and re-running the query before it, until the callback stops throwing or the timeout runs out. With then, my callback runs exactly once with whatever was there at that moment. So for anything that might not be ready yet, should is the right one. A good example is checking that a price list, which loads asynchronously, is sorted from low to high. There's no built-in assertion for that, so I write a callback that reads every price and expects the list to equal a sorted copy of itself. Because Cypress may call that callback many times, I keep it pure: just reading values and expect statements, no cy commands and nothing that changes state."

Code:
cy.get('[data-cy=price]').should((prices) => {
  const values = [...prices].map((el) => Number(el.innerText.replace(/[^0-9.]/g, '')))
  const sorted = [...values].sort((a, b) => a - b)
  expect(values).to.deep.equal(sorted)
})
Red flag to avoid:

Using .then for a check on data that is still loading, then blaming Cypress when it fails now and then.

They may ask next:
  • Why is it a problem to click a button inside a .should callback?
  • How would you check the list is still sorted after the user switches to high-to-low?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

12. Sometimes a cookie consent banner shows up and sometimes it doesn't. How do you write a Cypress test that copes with that?

What the interviewer is really testing:
Whether you know why conditional testing is fragile and prefer to control the state rather than branch on it.
Answer frame:

Why it's risky: the DOM can change right after you check, so 'if visible, click' races the app.

Best: make the state fixed by setting the consent cookie or storage key before the app loads.

Separate test: one test clears it and checks the banner itself.

Last resort: wait for a signal that the page has settled, then inspect the DOM once in .then.

Sample spoken answer:

"My first move is to remove the condition, not code around it. Conditional testing is risky in Cypress because the page can change right after I look. The banner might be about to render when I check, so the test skips it and then fails on a covered button. So I make the state fixed. If the banner is driven by a consent cookie or a local storage key, I set that before the app loads, for example in the onBeforeLoad option of cy.visit, and the banner never shows. Then I write one separate test that starts without consent and checks the banner appears and works. If I truly can't control it, say it comes from a third-party script, I wait for something that proves the page has settled, then read the body once inside then and branch on whether the banner exists. But that's a last resort, and I'd note it as a known risk."

Code:
cy.visit('/', {
  onBeforeLoad(win) {
    win.localStorage.setItem('cookieConsent', 'accepted')
  },
})
cy.get('[data-cy=cookie-banner]').should('not.exist')
Red flag to avoid:

Wrapping steps in if-visible checks so the test passes whether or not the feature works.

They may ask next:
  • Why does checking for the banner and then clicking it still fail now and then?
  • How would you test the banner itself, including what happens after the user declines?
Say it in 60 seconds

Network Stubbing 4 questions

Medium Coding round Fresher, Mid-level Practice question

13. Using cy.intercept, stub the products API with a fixture and check the page renders it. What's happening behind the scenes?

What the interviewer is really testing:
Whether you can stub a request correctly, in the right order, and explain how the stub reaches the app.
Answer frame:

Register first: set up the intercept before the action that fires the request.

Stub: match method and path, reply with a fixture file or an inline body.

Wait and assert: alias it, wait on it, then check what the user sees.

Behind it: the Cypress proxy sees the browser's requests and answers matching ones itself.

Sample spoken answer:

"I register the intercept before cy.visit, because the request fires as the page loads, and an intercept only catches requests made after it exists. I match GET on the products path and tell Cypress to reply with a fixture file, so the app gets a known list of three products without the real backend being involved. I alias it, wait on the alias, and then check the page shows three cards and the first product's name. Behind the scenes, the browser's traffic goes through a proxy run by the Cypress Node process, so when a request matches a route, the proxy answers it instead of passing it on. That's why it works for fetch and XHR alike, and the app has no idea it's stubbed. The payoff is a fast, repeatable test of the front end, including edge cases like an empty list that are hard to arrange with real data."

Code:
it('shows the product list', () => {
  cy.intercept({ method: 'GET', pathname: '/api/products' }, { fixture: 'products.json' }).as('getProducts')
  cy.visit('/shop')
  cy.wait('@getProducts')
  cy.get('[data-cy=product-card]').should('have.length', 3)
  cy.get('[data-cy=product-card]').first().should('contain', 'Desk Lamp')
})
Red flag to avoid:

Registering the intercept after cy.visit and then adding a fixed wait when the stub doesn't seem to apply.

They may ask next:
  • How would you stub the same endpoint to return an empty list in a different test?
  • What's the difference between an intercept that stubs and one that only watches the real request?
Say it in 60 seconds
Medium Coding round Mid-level, Senior Practice question

14. How do you check that the app sends the right data when a form is submitted, without faking the server's response?

What the interviewer is really testing:
Whether you can use intercept as a spy to test the contract between front end and API, not just what appears on screen.
Answer frame:

Spy: cy.intercept with only a matcher lets the request reach the real server and records it.

Assert: wait on the alias and check request.body, headers and the response status.

Change if needed: a handler can alter the request or response with req.continue or req.reply.

Sample spoken answer:

"If I call cy.intercept with just the method and URL and no response, it acts as a spy. The request goes to the real server, and Cypress records both the request and the response. So I set it up, fill the form, submit, and then wait on the alias. The wait yields the interception, and I can assert on request.body to check the payload has the right fields, maybe that the email was trimmed or the date was sent in the right format, and on the response status to check the server accepted it. This catches bugs the screen hides, like a field that's shown but never sent. If I want to see how the app handles something unusual while still hitting the real backend, I can pass a handler and use req.continue with a callback to change the response before the app receives it."

Code:
cy.intercept('POST', '/api/signup').as('signup')
cy.get('[data-cy=email]').type('  lee@example.com ')
cy.get('[data-cy=submit]').click()
cy.wait('@signup').then(({ request, response }) => {
  expect(request.body.email).to.equal('lee@example.com')
  expect(response.statusCode).to.equal(201)
})
Red flag to avoid:

Only checking the success message on screen and assuming the right data reached the server.

They may ask next:
  • How would you prove a request was not sent at all, for example when client-side validation fails?
  • If the same request fires twice, how do you check the second one?
Say it in 60 seconds
Medium Technical round Fresher, Mid-level, Senior Practice question

15. How would you test what the UI does when the API returns a 500, drops the connection, or responds very slowly?

What the interviewer is really testing:
Whether you test unhappy paths that are hard to cause on a real server, and keep the fake responses realistic.
Answer frame:

Error status: reply with statusCode: 500 and a realistic body, then check the message and retry.

Network failure: forceNetworkError: true makes the request fail like a dropped connection.

Slow: add a delay to the reply, then check the loading state and double-submit protection.

Realism: error bodies copied from what the real API sends.

Sample spoken answer:

"This is where stubbing earns its keep, because these states are hard to cause on a real server on demand. For a server error, I intercept the call and reply with statusCode 500 and the error body the backend really sends, then check the user sees a sensible message instead of a blank screen, and that retry works. For a dead connection, intercept supports forceNetworkError, which makes the request fail the way it does when the network drops. For slowness, I add a delay to the stubbed reply, then assert the spinner shows, the submit button is disabled so it can't be pressed twice, and everything settles once the response lands. I keep those stubbed bodies in fixtures that match the real API's error format, otherwise I'm testing a response the app will never get."

Code:
cy.intercept('GET', '/api/orders', {
  statusCode: 500,
  body: { error: 'Internal error' },
}).as('orders')
cy.visit('/orders')
cy.wait('@orders')
cy.get('[data-cy=error-message]').should('be.visible')

// in another test: a slow but successful reply
cy.intercept('GET', '/api/orders', { fixture: 'orders.json', delay: 2000 })
Red flag to avoid:

Only ever testing the happy path because the real backend never fails in the test environment.

They may ask next:
  • How do you make sure your stubbed error body matches what the real API sends?
  • What would you check if the user clicks submit twice while the request is slow?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

16. Tell me about a time stubbed network responses in your Cypress tests hid a real bug. What did you change afterwards?

What the interviewer is really testing:
Whether you understand the trade-off of stubbing and have a way to keep fake responses honest.
Answer frame:

What happened: tests green, bug found later.

Why stubs hid it: the fixture no longer matched the real API.

Change: some real-backend tests for key journeys, and fixtures checked against the live shape.

Sample spoken answer:

"We had a product list page covered by stubbed tests that were always green. Then the backend team renamed a field from price to unitPrice, and in staging every product showed an empty price. Our fixture still had the old field name, so the tests were checking the front end against an API that no longer existed. I changed two things. First, I added a small set of tests that run against the real backend with no stubs for the key journeys, browse, add to cart and checkout, so a contract change breaks something. Second, we started refreshing the main fixtures from real API responses with a script in CI, and the build fails if the shape changes and the fixture wasn't updated. Stubs stayed for edge cases like errors and empty lists, which is where they're most useful."

Red flag to avoid:

Claiming stubbing has no downside, or dropping stubs entirely after one incident.

They may ask next:
  • How do you decide which tests use the real API and which use stubs?
  • Who should own the fixtures when the API changes?
Say it in 60 seconds

Test Data 3 questions

Easy Technical round Fresher Practice question

17. What are fixtures in Cypress, how do you load one, and when is a fixture the wrong tool?

What the interviewer is really testing:
Whether you know fixtures are static files for known data, and don't confuse them with creating data in the application.
Answer frame:

What: static files, usually JSON, in the fixtures folder holding known data.

Use: cy.fixture() in a test or hook, or { fixture: 'file.json' } as an intercept reply.

Wrong tool: data that must exist in the real backend, or must be unique per run.

Sample spoken answer:

"Fixtures are static data files, mostly JSON, kept in the fixtures folder of the Cypress project. I use them in two ways. One is to load test input, like a set of user details, with cy.fixture in a beforeEach, and then use those values when filling forms. The other, which I use more, is as a canned API response in cy.intercept, so the front end always gets the same data. They're the wrong tool when the data has to really exist in the backend, like a user who needs to log in, because a JSON file doesn't create anything. That needs an API call or a task that seeds the database. They're also wrong for values that must be unique on each run, like a new sign-up email, where I'd generate one with a timestamp. And I keep fixtures small and close to real responses so they don't drift."

Red flag to avoid:

Thinking a fixture puts data into the application's database.

They may ask next:
  • How do you stop a fixture from drifting away from what the real API returns?
  • How would you reuse one fixture but change a single field for one test?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

18. Each test needs a user with a specific set of orders. How do you create that data before the test, and where does that code run?

What the interviewer is really testing:
Whether you set up state quickly and reliably outside the UI, and know which parts run in the browser and which in Node.
Answer frame:

Through the API: cy.request calls an endpoint directly, not through the page, so it's quick and CORS doesn't apply.

Through Node: cy.task runs a function registered in setupNodeEvents, for direct database work.

Keep it clean: seed in beforeEach, make data unique or reset it, never click through the UI to build it.

Sample spoken answer:

"I don't create setup data through the UI, because it's slow and a bug in that screen would fail every unrelated test. I have two options. If the backend has an API for it, or a test-only endpoint, I use cy.request in a beforeEach. It makes the HTTP call directly rather than from the page, so it's quick and not tied to any screen. If I need to go straight to the database, say to reset tables or insert rows there's no API for, I use cy.task. A task is a function I register in setupNodeEvents in the config file, and it runs in the Node process, so it can use a database client. One detail that catches people: a task must return a value, a promise or null, because returning undefined makes Cypress fail it. I also make each test's data unique or reset it, so tests don't trip over each other."

Code:
// cypress.config.js
const { defineConfig } = require('cypress')
const db = require('./test/db')

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      on('task', {
        seedOrders({ email, count }) {
          return db.createUserWithOrders(email, count)
        },
      })
    },
  },
})

// in the spec
beforeEach(() => {
  cy.task('seedOrders', { email: 'kim@example.com', count: 3 })
})
Red flag to avoid:

Logging in and creating every record through the UI at the start of each test.

They may ask next:
  • What's the risk of a test-only seeding endpoint, and how do you keep it out of production?
  • How is cy.request different from cy.intercept?
Say it in 60 seconds
Medium Coding round Mid-level, Senior Practice question

19. Logging in through the UI before every test is making the suite slow. How do you fix that in Cypress?

What the interviewer is really testing:
Whether you know programmatic login and session caching, while still keeping the real login form covered.
Answer frame:

Keep one UI test: a dedicated spec still covers the real login form.

Programmatic login: everywhere else, log in with cy.request and skip the form.

Cache it: cy.session saves cookies and storage under a key, restores them later, and re-creates them if validate fails.

Sample spoken answer:

"I keep one or two tests that log in through the form, because the login page itself needs testing. Everywhere else, the form is wasted time. First I switch to a programmatic login: a cy.request to the login endpoint, which sets the session cookie or returns a token I can store. Then I wrap that in cy.session. It takes an id, like the user's email, and a setup function. The first time, it runs setup and saves the cookies, local storage and session storage. Next time a test asks for the same id, it restores them instead of logging in again. I also pass a validate function that checks the session still works, for example that the profile endpoint answers 200, so an expired token gets recreated instead of failing strangely. And because the page is cleared after the session step, I visit the page afterwards."

Code:
Cypress.Commands.add('login', (email, password) => {
  cy.session(
    email,
    () => {
      cy.request('POST', '/api/login', { email, password })
    },
    {
      validate() {
        cy.request('/api/me').its('status').should('eq', 200)
      },
    }
  )
})

beforeEach(() => {
  cy.login('kim@example.com', 'test-password')
  cy.visit('/dashboard')
})
Red flag to avoid:

Turning off test isolation so one login carries through every later test.

They may ask next:
  • Why do you call cy.visit after cy.session and not before it?
  • How would you keep a cached session across different spec files?
Say it in 60 seconds

Custom Commands 3 questions

Medium Coding round Mid-level, Senior Practice question

20. Write a custom command for a step your tests keep repeating, and show how you'd make TypeScript aware of it.

What the interviewer is really testing:
Whether you can build reusable, reliable steps and wire them into a typed project, and know when a plain function is better.
Answer frame:

Define: Cypress.Commands.add in the support commands file, which loads before every spec.

Make it reliable: scoped selectors and a wait on the request inside the command.

Type it: extend the Chainable interface in the Cypress namespace.

Scope: commands for user-level steps in the chain; plain functions for pure helpers.

Sample spoken answer:

"Say lots of tests add an item to the cart: find the product card, click add, wait for the cart request. I put that in the support commands file with Cypress.Commands.add, name it addToCart, and have it take the product name. Inside, I follow the same practices as in tests: a scoped selector, an intercept alias and a wait on it, so every caller gets a reliable step. Because commands are added at runtime, TypeScript doesn't know about them, so I add a declaration that extends the Chainable interface in the Cypress namespace. Then cy.addToCart autocompletes and gets type-checked. I don't turn everything into a command, though. If something doesn't need to be in the Cypress chain, like building a test user object, a normal imported function is simpler and easier to test."

Code:
// cypress/support/commands.ts
Cypress.Commands.add('addToCart', (productName: string) => {
  cy.intercept('POST', '/api/cart').as('addToCart')
  cy.contains('[data-cy=product-card]', productName)
    .find('[data-cy=add-to-cart]')
    .click()
  cy.wait('@addToCart')
})

declare global {
  namespace Cypress {
    interface Chainable {
      addToCart(productName: string): Chainable<void>
    }
  }
}

export {}
Red flag to avoid:

Hiding assertions and branching logic inside commands so a failure no longer shows which step broke.

They may ask next:
  • When would you write a child command that works on the previous subject?
  • How do you overwrite a built-in command, and why is that risky?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

21. Would you use the Page Object Model in a Cypress project, custom commands, or something else? Defend your choice.

What the interviewer is really testing:
Whether you can reason about test structure for this tool's queued model, rather than copying a pattern from another framework.
Answer frame:

Page objects: fine if thin: selectors and small methods that return cy chains.

Custom commands: fit shared steps that cross pages, like login.

App actions: set state directly through the API or the app, skipping UI covered elsewhere.

Choice: a mix, judged by readability and speed.

Sample spoken answer:

"I don't treat it as either-or. Page objects work in Cypress if they're thin: a module per page holding selectors and small methods that return cy chains, so tests read like user steps. They go wrong when they hold elements in fields, hide assertions or try to keep state, because Cypress commands are queued, not immediate, so a stored element is just a chainable from some earlier moment. For steps that cross pages, like login or filling a cart, custom commands fit better. The third idea is app actions: instead of clicking through screens to reach a state, I set it directly, through the API, or where the app exposes helpers in test builds, through its own functions on the window. That's much faster, and fine because that UI flow has its own test. So on a real project I'd mix: thin page modules for selectors, commands for shared steps, API or app actions for setup."

Red flag to avoid:

Copying a page object that stores elements in fields, which breaks with Cypress's queued commands.

They may ask next:
  • What's the risk of exposing app functions on the window just for tests, and how do you contain it?
  • How do you stop page objects from growing into one giant file?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

22. Describe a Cypress suite you set up or inherited. What was slowing the team down, and what did you change first?

What the interviewer is really testing:
Whether you improve a suite by measuring and prioritising, not by rewriting everything at once.
Answer frame:

Starting point: size, run time and how much the team trusted it.

Problems: the two or three biggest, with evidence.

Changes: in priority order, and why that order.

Result: run time, stability and whether people read failures again.

Sample spoken answer:

"At my last company I inherited about eighty specs that took over forty minutes in CI and failed often enough that developers ignored them. I spent the first week reading failures instead of writing tests. Three things stood out: every test logged in through the UI, most setup data was created by clicking through admin screens, and selectors were long CSS paths. I fixed login first, since it touched everything, with a programmatic login command wrapped in cy.session. Then I moved data setup to API calls behind a custom command and a couple of tasks. Selectors I changed gradually, asking developers to add data-cy attributes as they touched each screen. Run time came down to around fifteen minutes before we added any parallel machines, and once failures meant real bugs, developers started reading them again, which was the real win."

Red flag to avoid:

Starting with a full rewrite without measuring what was actually wrong.

They may ask next:
  • How did you get developers to add the test attributes?
  • What did you decide not to fix, and why?
Say it in 60 seconds

Component Testing 2 questions

Easy Technical round Fresher, Mid-level Practice question

23. What is component testing in Cypress, and how is it different from an end-to-end test and from a unit test in a Node test runner?

What the interviewer is really testing:
Whether you know where component tests sit between unit and end-to-end tests, and what each layer is good for.
Answer frame:

Component test: mounts one component in a real browser using the project's own build setup, with no full app.

vs end-to-end: e2e visits the running app and goes through routing, APIs and real pages.

vs Node unit test: those render into a simulated DOM; component tests get real layout, CSS and events.

Use: many states of one component, quickly; e2e for a few key journeys.

Sample spoken answer:

"A component test mounts a single component, say a date picker, in a real browser using the project's own bundler setup, with the props I choose. There's no full app, no routing and usually no backend; I stub whatever it calls. An end-to-end test is the opposite. It visits the running application and goes through real pages and APIs, so it catches integration problems but is slower and harder to set up for edge cases. Compared with a unit test in a Node runner that renders into a simulated DOM, a Cypress component test runs in an actual browser, so layout, CSS, focus and real events behave as they will for users, and I can watch it in the runner. I use component tests for the many states of one piece, like disabled, error or very long text, and keep end-to-end tests for a handful of journeys that matter most."

Red flag to avoid:

Saying component tests replace end-to-end tests, or that they run in a fake DOM.

They may ask next:
  • How do you mount a component that needs a router, a store or a theme provider around it?
  • Which tests would you move from end-to-end to component tests in a slow suite?
Say it in 60 seconds
Medium Coding round Mid-level, Senior Practice question

24. Write a Cypress component test for a quantity picker: it starts at 1, the plus button raises it, and it calls onChange with the new value.

What the interviewer is really testing:
Whether you can test both what the user sees and the component's contract with its parent.
Answer frame:

Mount: cy.mount with props, passing a cy.stub() as the callback.

Act and assert: click like a user, check the visible value.

Check the contract: assert the stub was called with the expected argument.

Sample spoken answer:

"In a React project, I mount the component with cy.mount and pass its props, including onChange set to a cy.stub with an alias, so I can check how it talks to its parent. Then I test it the way a user would: check the displayed value starts at 1, click plus, and check it shows 2. Last, I check the stub was called with 2. That covers both what the user sees and the component's contract with whoever uses it. I'd add a few more cases in the same file, like the minus button being disabled at 1 and the upper limit, because component tests are cheap enough to cover edge states that would be painful to reach in an end-to-end test. The mount command itself comes from the framework adapter, which the component testing setup registers in the component support file."

Code:
// QuantityPicker.cy.jsx, React adapter
import QuantityPicker from './QuantityPicker'

describe('QuantityPicker', () => {
  it('increments and reports the new value', () => {
    const onChange = cy.stub().as('onChange')
    cy.mount(<QuantityPicker initial={1} onChange={onChange} />)

    cy.get('[data-cy=qty-value]').should('have.text', '1')
    cy.get('[data-cy=qty-plus]').click()
    cy.get('[data-cy=qty-value]').should('have.text', '2')
    cy.get('@onChange').should('have.been.calledWith', 2)
  })
})
Red flag to avoid:

Only asserting the stub was called and never checking what the user actually sees.

They may ask next:
  • How would you test that the minus button can't go below 1?
  • What changes if the component fetches data when it mounts?
Say it in 60 seconds

CI & Parallel 3 questions

Medium Technical round Mid-level, Senior Practice question

25. How do you run a Cypress suite in a CI pipeline? Walk me through the steps and what you keep when a run fails.

What the interviewer is really testing:
Whether you've actually wired Cypress into a pipeline and know the practical traps: app start-up, caching and artifacts.
Answer frame:

Install: clean install, caching both node modules and the Cypress binary, which downloads separately.

Start and wait: start the app, wait until it answers, then cypress run, which is headless by default.

Config: base URL and secrets from CI environment variables, not the repo.

Artifacts: upload screenshots, videos if enabled, and a JUnit-style report.

Sample spoken answer:

"In CI I use cypress run, not open, so it runs headless and exits with a non-zero code when anything fails, which fails the job. The pipeline does a clean install, and I cache both the node modules and the Cypress binary cache, because the binary is a separate download and fetching it every time is slow. The app has to be up before tests start, so I either point at a deployed environment or start it in the job and wait until the URL responds, with a helper like start-server-and-test, instead of sleeping. Base URL and credentials come from CI environment variables. On failure, Cypress saves screenshots automatically in run mode, so I upload that folder and any videos as build artifacts, and I add a JUnit reporter so CI shows which tests failed. I also pin the browser with the browser flag so runs stay consistent."

Code:
npm ci
npx start-server-and-test start http://localhost:3000 'cypress run --browser chrome'
Red flag to avoid:

Using a fixed sleep to wait for the app to start, or trying to run cypress open in the pipeline.

They may ask next:
  • Why might a test pass in cypress open on your laptop but fail in CI?
  • How do you keep the Cypress version in CI the same as the one developers use?
Say it in 60 seconds
Hard System design round Senior Practice question

26. How does parallelisation work in Cypress, and how would you set it up for a suite of about 300 spec files?

What the interviewer is really testing:
Whether you know the unit of parallel work is the spec file and can design balanced, independent runs across machines.
Answer frame:

Unit of work: spec files, not single tests; one cypress run works through its specs one after another.

Across machines: several CI machines each take a share of the specs.

Balancing: recording to Cypress Cloud with --parallel hands out specs by past duration; without it, split by timing data yourself.

Design: break up giant specs, keep specs independent, give each machine its own data.

Sample spoken answer:

"The first thing to know is that Cypress parallelises by spec file, and a single cypress run goes through its specs one after another. So speed comes from running several CI machines at once, each taking a share. The built-in way is to record to Cypress Cloud and pass the parallel flag with the same build id on every machine; the service hands out specs using their past durations so the machines finish around the same time. Without that, I'd split specs myself with the spec flag, using timing data rather than alphabetical order. For 300 specs, I'd first look at spec durations, because one twenty-minute spec sets the floor however many machines I add, so I'd break the big ones up. Then I'd make sure specs share no data, for example each one creates its own users, and pick the machine count by watching where adding more stops helping."

Red flag to avoid:

Believing Cypress runs the tests inside one spec in parallel, or splitting specs without looking at how long each one takes.

They may ask next:
  • Why doesn't adding more machines keep making the run faster?
  • How do you stop two machines from editing the same test account at once?
Say it in 60 seconds
Hard Situational round Senior Practice question

27. Developers say they won't wait 45 minutes for Cypress on each pull request. How would you give them fast, trustworthy feedback?

What the interviewer is really testing:
Whether you cut run time by removing waste and choosing the right test layer, not only by buying more machines.
Answer frame:

Measure: slowest specs, and how much of each test is setup.

Cut waste: programmatic login and seeding, no fixed waits, edge cases moved to component or API tests.

Tier it: a smoke set on every pull request, the full suite on merge or nightly, both in parallel.

Keep trust: quarantine known flaky tests, and track them.

Sample spoken answer:

"I'd start with data, not machines. I'd list specs by duration and look at how much of each test is setup. Usually the biggest wins are logging in through the UI, creating data by clicking, and fixed waits, and fixing those alone can cut the time a lot. Next I'd look at what's tested end to end that doesn't need to be. Form validation messages, or every state of a widget, belong in component or API tests that run in seconds. Then I'd split the suite. A smoke set covering the main journeys runs on every pull request across a few parallel machines and should finish in minutes. The full suite runs on merge to main and nightly, also in parallel. Finally, I'd quarantine known flaky tests so they don't block pull requests, but track them, because a fast suite nobody trusts is still useless."

Red flag to avoid:

Only asking for more CI machines, or deleting slow tests without checking what coverage goes with them.

They may ask next:
  • How do you choose which tests go into the pull request smoke set?
  • What happens when a bug slips through because its test only runs nightly?
Say it in 60 seconds

Flaky Tests 3 questions

Medium Technical round Mid-level, Senior Practice question

28. A Cypress test goes red roughly once in ten CI runs. How do you work out why, and what are the usual culprits?

What the interviewer is really testing:
Whether you debug intermittent failures from evidence and know the patterns that cause them in Cypress specifically.
Answer frame:

Evidence: the failure screenshot, video, error message and history across runs.

Usual causes: racing a request, re-renders or animation, shared or leftover data, fixed waits, order dependence.

Reproduce: repeat the test many times, alone and inside the full spec, with key requests slowed down.

Fix the cause: alias waits, better assertions, unique data; retries only as a safety net.

Sample spoken answer:

"First I collect evidence instead of rerunning and hoping. I look at the failure screenshot, the video if there is one, and the error: did it time out finding something, click the wrong thing, or see stale data? Then I look for a pattern across runs, like only on one machine or only after a certain spec. The usual culprits are a test racing a request it never waited for, an element that re-renders or animates while being clicked, data shared with another test or left over from an earlier run, a fixed wait that's sometimes too short, and a test relying on order. To reproduce, I run it many times in a loop, which the bundled lodash times helper makes easy, and I slow the key requests down with a delay on an intercept to widen the race. Once I see the cause, I fix it, usually with an intercept alias, a stronger assertion before acting, or unique data. Retries come afterwards, as a safety net, not the fix."

Code:
Cypress._.times(25, (i) => {
  it('applies a coupon at checkout, run ' + (i + 1), () => {
    // same steps as the flaky test
  })
})
Red flag to avoid:

Rerunning until green, or adding a longer wait without knowing what it was waiting for.

They may ask next:
  • How do you tell a flaky test apart from a flaky application?
  • When is it right to quarantine a test instead of fixing it straight away?
Say it in 60 seconds
Hard Behavioral round Mid-level, Senior Practice question

29. Tell me about a Cypress test that failed in CI but never on your own machine. What was really going on?

What the interviewer is really testing:
Whether you debug from evidence and understand how CI differs from a laptop in speed, screen size and data.
Answer frame:

Situation: the test, how often it failed and where.

Evidence: what the screenshot, video or logs showed.

Cause and fix: the real difference between CI and local, and the change you made.

Proof: repeated runs, and how you stopped the pattern coming back.

Sample spoken answer:

"At my last company, a checkout test passed every time for me but failed in CI a few times a week, always at the step that applies a discount code. The failure screenshot showed the old total still on screen. The video made it clear: the CI machine was slower, and the test typed the code and checked the total before the pricing request had come back. On my laptop the request was fast enough that it never showed up. There was also a debounce on the input, so the request only fired after typing stopped. The fix was to intercept the pricing call, wait on its alias, and only then assert the new total. I proved it by running that spec in a loop fifty times on the CI runner, and it passed every time. Then I searched the suite for the same pattern and fixed four more tests."

Red flag to avoid:

A story where the fix was a longer wait or turning on retries, with no explanation of the cause.

They may ask next:
  • Which differences between a CI machine and a laptop do you check first?
  • How did you stop the same pattern creeping into new tests?
Say it in 60 seconds
Medium Situational round Mid-level, Senior Practice question

30. We ship in the morning. A checkout spec has gone red twice this week, and a teammate proposes three retries across the whole suite. How do you respond?

What the interviewer is really testing:
Whether you can protect the release without hiding a real bug or teaching the suite to lie.
Answer frame:

Look first: is it the test or the app? Read the failures before deciding.

Short term: a retry on that one test, in run mode only, with a ticket and an owner.

Why not global: suite-wide retries hide new flakes and real intermittent bugs.

After release: fix the cause and remove the retry.

Sample spoken answer:

"I'd first spend twenty minutes on the failures themselves. If the screenshots show the app doing something wrong, like a wrong total or a double submit, that may be a real intermittent bug, and it matters more than the date, so I'd raise it with the lead tonight. If it's clearly the test, say it races a request, I'm fine with a retry on that one test in run mode, with a ticket and an owner. What I'd push back on is three retries across the whole suite. That quietly hides every new flaky test and every real bug that only shows up sometimes, and nobody notices because the report is green. A small global retry can be fine later, but only if tests that pass on retry are tracked and reviewed. After the release, I'd fix the cause and take the retry off."

Code:
// temporary: pricing race, tracked in the flaky-tests ticket
it('applies a discount code', { retries: { runMode: 2, openMode: 0 } }, () => {
  // test steps
})
Red flag to avoid:

Agreeing to global retries without looking at the failures, or blocking the release over a test nobody has investigated.

They may ask next:
  • How would you track which tests only pass on a retry?
  • What do you do if the lead says ship anyway?
Say it in 60 seconds
Were you asked something else? Share it A person checks every question before it goes on the site. No name is shown.
For the call itself

The questions above are the prep. The call has ten more.

ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your resume and notes are never stored on our servers. It stays out of screen share on every plan; only you can see it.

Download ClapAssist with 10 free minutes
Mac and Windows · Stays out of screen share · No card