Resource library

Automation Interview

Cypress Interview Questions and Answers

Cypress interview questions with sample answers on its retry-able command chain, async model, cy.intercept stubbing, custom commands, and real limitations.

2,318 words | Article schema | FAQ schema | Breadcrumb schema

Overview

Cypress interviews have a signature: they look easy and then quietly test whether you understand what makes Cypress unusual. Candidates who learned it as write cy dot something and it works get exposed the moment an interviewer asks why you cannot save cy.get into a variable and use it later, or why await does not behave the way they expect. The tool hides its mechanics behind a friendly API, and the interview is where those mechanics get pulled into the light.

This guide is built around the Cypress mental model rather than a flat list. We cover how it runs inside the browser, why the command chain retries, where the async confusion comes from, how cy.intercept changed network testing, and the limitations a senior interviewer expects you to name honestly. Every question includes an answer you can say out loud, phrased the way an engineer who has shipped Cypress suites would phrase it.

If your target role mixes Cypress with API or component testing, the later sections cover those too, because modern Cypress interviews rarely stay purely end-to-end.

Why Cypress Questions Trip People Up

Cypress runs in the same run loop as your application, inside the browser, not as an external process driving it over a wire protocol. That single architectural fact explains most of the tricky interview questions: the retry-ability, the strange relationship with async, the same-origin history, and the lack of native multi-tab control. If you can articulate that Cypress executes alongside the app it tests, you can reason your way to the answer for most follow-ups instead of memorizing them.

The interviewer is usually checking one thing: do you understand that Cypress commands are not promises and not synchronous, but a queue of chainable actions that Cypress enqueues and then runs. Get that right and the rest of the interview flows.

The Architecture Question

Q: How is Cypress architecturally different from Selenium? Selenium sends commands to a browser driver over the WebDriver protocol from an external process in your language of choice. Cypress bundles and executes inside the browser, in the same event loop as the application under test, which gives it native access to everything the app does: the DOM, network layer, timers, and storage. That proximity is why Cypress can automatically wait, snapshot state for time travel, and stub network calls without a proxy. The same proximity is also the source of its constraints, because it is bound by what the browser sandbox allows a page to do.

A strong answer names both sides: the in-browser design buys reliability and debuggability, and it costs you things an out-of-process driver does like easy multi-browser-window control and arbitrary cross-origin navigation. Interviewers reward candidates who present it as a deliberate trade-off, not as Cypress is better or worse.

Retry-Ability And The Command Chain

Q: What is retry-ability in Cypress? Cypress commands that query the DOM, like cy.get and cy.contains, plus their assertions, automatically retry until they pass or hit a timeout. So cy.get('.cart').should('have.length', 3) keeps re-querying until three items exist or the four-second default elapses. This is why you rarely need explicit waits: the assertion is the wait. Only the last query in a chain retries, and only queries retry, not actions like cy.click, which is a common gotcha.

Q: Why can you not assign cy.get to a variable and reuse it? Because Cypress commands are asynchronous and enqueued, not executed immediately. cy.get does not return the element, it schedules a command whose result is only available inside a .then callback or through the next chained command. If you write const el = cy.get('.btn') you get a chainer object, not the element. The correct patterns are chaining, using .then to work with the yielded subject, or using an alias with cy.get('@myAlias'). Explaining this clearly is often the single most decisive moment in a Cypress interview.

  • Only queries retry, and only the last one in the chain; actions like click do not retry.
  • The assertion is the wait, which is why explicit sleeps are usually unnecessary.
  • cy.get returns a chainer, not an element; use .then or aliases to access the value.
  • Use cy.wrap to bring a non-Cypress value into the command chain.

The Async Confusion Question

Q: Can you use async and await with Cypress? Not the way you use them with a normal promise, and a good answer explains why rather than just saying no. Cypress commands are not real promises: they do not resolve when you await them, they enqueue onto Cypress's internal command queue. Mixing native async and await, setTimeout, or a raw promise with cy commands breaks the queue's ordering guarantees because your code runs immediately while the cy commands run later. When you genuinely need to work with the yielded value, you stay inside the Cypress world with .then. If you must call an external async API, wrap the returned promise with cy.wrap(promise) so Cypress manages it in the queue.

The interviewer is listening for the phrase they are not promises. Candidates who say Cypress uses promises so I just await them reveal they have not hit the wall this design creates, and that is a signal against them.

Network Stubbing With cy.intercept

Q: How do you stub and spy on network requests? cy.intercept is the modern command. As a spy, cy.intercept('GET', '/api/cart').as('getCart') lets you cy.wait('@getCart') and then assert on the real request and response. As a stub, you pass a response so cy.intercept('GET', '/api/cart', { fixture: 'cart.json' }) returns canned data, which makes error states, empty states, and slow responses deterministic. You can also delay, force status codes, or modify the request in flight.

Q: Why is cy.wait on an alias better than cy.wait with a number? Because waiting a fixed number of milliseconds is guessing: too short and the test flakes, too long and every run wastes time. Waiting on an intercept alias waits for the exact event, the response arriving, so the test is both faster and stable. I treat any cy.wait(1000) in a review as a red flag to replace with an aliased intercept or a retrying assertion.

  • cy.intercept spies when you alias and wait, stubs when you supply a response.
  • Use fixtures for canned responses and to reproduce error and empty states.
  • Prefer cy.wait('@alias') over cy.wait(number) so you wait for the real event.
  • You can force status codes, delays, and network failures to test resilience.

Custom Commands, Fixtures, And Structure

Q: When do you write a custom command versus a plain function? Add a custom command with Cypress.Commands.add when the behavior belongs in the command chain and yields a subject other tests will build on, like cy.login() that seeds a session. Use a plain helper function for pure logic that does not need to chain. Overusing custom commands for everything makes a codebase hard to follow, so I reserve them for genuinely reusable, chainable actions and keep the rest as functions or page objects.

Q: How do you handle login efficiently? Logging in through the UI in every test is slow and flaky. Modern Cypress offers cy.session to cache and restore session state across tests so the login runs once and is reused, and where possible I authenticate through an API request and set the token or cookie directly rather than driving the form. That keeps the login flow itself covered by one dedicated test and out of the critical path of every other spec.

Limitations You Should Name Honestly

Q: What are Cypress's limitations? A senior answer names them without defensiveness. Historically Cypress restricted a test to a single superdomain, though cy.origin now handles cross-origin flows like third-party auth. It does not natively drive multiple browser tabs, because a page cannot control another tab, so you test the behavior rather than the second tab, for example by asserting the link's target or visiting the URL directly. It is JavaScript and TypeScript only, which matters for teams standardized on Java or C#. And because it runs in the browser, some low-level or native-OS interactions are out of reach.

The reason this question matters is trust. An engineer who can only list strengths has not used the tool under pressure. Pairing each limitation with the workaround you actually use, cy.origin, testing target attributes, API-based setup, shows you have shipped real suites.

  • Cross-origin is handled with cy.origin, not by pretending it is not a constraint.
  • No native multi-tab control; assert link targets or visit URLs directly.
  • JavaScript and TypeScript only, a real factor for Java or C# shops.
  • Some native and OS-level interactions are outside the browser sandbox.

Coding And Framework Questions

Q: Write a test for a login form including a failure case. Talk through it: cy.visit the page, cy.get the fields by a stable data attribute, type credentials, submit, then assert the URL changed and a welcome element is visible. For the failure path, intercept the auth call to return 401, submit bad credentials, and assert the error message appears and the user stayed on the login page. Naming the negative case unprompted signals test-design maturity.

Q: How do you keep Cypress tests independent? Each test should set up its own state and not depend on order. I seed data through API calls or tasks in a beforeEach, use cy.session for auth, and avoid relying on data left behind by a previous test. Independence is what lets the suite run in parallel and lets a single failure be meaningful rather than a cascade.

Component Testing And CI

Q: What is Cypress component testing? It mounts a single UI component in a real browser with cy.mount, so you test it in isolation with real rendering and events, faster than a full end-to-end visit and closer to a unit. It suits component-library work and edge states that are painful to reach through a full app flow. Q: How do you run Cypress in CI? Run cypress run headless, split the spec files across parallel machines, record artifacts like screenshots and videos on failure, and fail the build on any failing spec. I gate merges on a fast smoke subset and run the full suite on a schedule or post-merge so pull requests stay quick.

Rapid-Fire Cypress Follow-Ups

Breadth questions come fast at the end of a Cypress round. How do you pass data from the browser to Node, for example to query a database or read a file? cy.task, which runs code in the Node process and returns a value back into the command chain. How do you set environment-specific values? Cypress.env together with the env block in the config, read with Cypress.env('key'), so URLs and credentials are never hardcoded in specs. How do you change the viewport for responsive checks? cy.viewport. How do you retry a failed test? Configure retries in the config with separate runMode and openMode counts, but treat retries as a safety net while you fix the underlying flake, not as the fix itself.

A couple more favorites separate real users from tutorial followers. What is the difference between cy.get and cy.find? cy.get queries from the document root, while cy.find searches within a previously yielded subject, so you chain it after another query to scope the search. When do you use before versus beforeEach? before runs once per spec for expensive one-time setup, and beforeEach runs before every test for per-test isolation like seeding data or restoring a session with cy.session. Answering these crisply shows you have actually maintained a Cypress project.

  • cy.task runs code in Node to reach a database, filesystem, or external service.
  • Cypress.env and the config env block keep URLs and secrets out of the specs.
  • cy.get queries from the root; cy.find scopes within the current subject.
  • before runs once per spec; beforeEach runs before every test for isolation.

Frequently Asked Questions

Are Cypress commands promises?

No. They look promise-like because you chain them, but they are enqueued onto Cypress's internal command queue and do not resolve when awaited. Use .then to access a yielded value and cy.wrap to bring an external promise into the chain.

Why can't I store cy.get in a variable?

Because cy.get returns a chainer object, not the DOM element, and runs asynchronously later. Assigning it does not give you the element. Access the value inside a .then callback or create an alias with .as and retrieve it with cy.get('@alias').

How does cy.intercept differ from the old cy.route?

cy.intercept is the modern, more powerful command that replaced the deprecated cy.route. It can spy, stub, delay, modify requests and responses, and handle all request types, not just XHR, giving you full control over the network layer in tests.

Can Cypress test multiple tabs?

Not natively, because a page cannot control a separate browser tab. The usual approach is to test the behavior instead: assert the link's target attribute, or visit the destination URL directly with cy.visit rather than opening a real second tab.

How do you avoid flaky waits in Cypress?

Lean on retry-ability by asserting the end state, and replace cy.wait with a number by waiting on an aliased intercept. The assertion or the network event becomes the wait, which is faster and far more stable than guessing a fixed duration.

Is Cypress better than Selenium?

It depends on the context, and saying so is the strong answer. Cypress gives a better developer experience and reliability for modern web apps in JavaScript teams. Selenium wins on language choice, cross-browser breadth, and multi-window scenarios. Frame it as a trade-off, not a verdict.

Related QAJobFit Guides