Resource library

Automation Interview

Cypress Interview Questions and Answers for 2026 Interviews

Cypress interview questions and answers for 2026, with 60 expert Q&As on architecture, retries, intercepts, component testing, debugging, and CI.

55 min read | 7,744 words

TL;DR

Prepare around architecture, the command queue, retry-ability, network control, sessions, component testing, isolation, CI, and tool-choice tradeoffs. Strong answers explain the mechanism, show a production example, and name a failure mode.

Key Takeaways

  • Explain the queued command model before discussing Cypress syntax.
  • Separate query retry-ability from whole-test retries.
  • Synchronize with observable application state or aliased network events.
  • Use component, API, and E2E tests at the smallest realistic risk boundary.
  • Design identities, data, specs, and artifacts for parallel execution.
  • Compare Cypress with Playwright through requirements and tradeoffs.
  • Diagnose the first failed attempt before changing timeouts or retry counts.

Cypress interview questions and answers for 2026 interviews should test more than command recall. A strong candidate can explain Cypress architecture, reason about its queued command model, write deterministic network and component tests, diagnose flake, and decide when another tool better fits the risk. This guide gives you 60 fully answered questions, practical code, grading criteria, and a study path for junior through senior SDET roles.

Use the answers as speaking models, not scripts to memorize. In an interview, begin with the direct answer, explain the mechanism, provide one production example, and finish with a tradeoff or diagnostic step. That structure shows both knowledge and judgment.

TL;DR

Topic Question count Difficulty
Cypress Fundamentals and Architecture 6 Beginner
Command Queue, Retry-ability, and Assertions 6 Intermediate
Selectors, DOM, and User Interactions 6 Beginner to Intermediate
Network Testing, APIs, and Stubbing 6 Intermediate
Test Data, Fixtures, Authentication, and Sessions 6 Intermediate
Configuration, Custom Commands, Plugins, and TypeScript 6 Intermediate to Advanced
Component Testing and Test Boundaries 6 Intermediate
Retries, Flake, Debugging, and CI 6 Advanced
Cross-Origin, Browser Constraints, and Tool Choice 6 Advanced
Scenario Design, Leadership, and Framework Evolution 6 Advanced

The highest-value concepts are command queuing, query retry-ability, actionability, cy.intercept(), cy.session(), test isolation, component boundaries, and CI evidence. Practice explaining why a design is reliable, not only showing syntax.

1. Cypress Fundamentals and Architecture: Cypress Interview Questions and Answers

Q: What is Cypress, and where does it fit in a test strategy?

Cypress is a JavaScript and TypeScript testing platform for browser-based end-to-end and component tests. It runs a Node process alongside browser code and gives tests direct, instrumented access to the application, DOM, network, storage, and timers. Position Cypress as one layer: keep business logic in unit tests, components in component tests, a focused set of user journeys in E2E tests, and API checks where the browser adds no value. A checkout tax calculation belongs below the browser, while the rendered total and payment handoff deserve browser coverage. That allocation keeps feedback quick without losing confidence in the customer journey. Cypress also supports direct API setup, so an E2E test need not repeat slow UI preparation.

Q: How does Cypress architecture differ from Selenium?

Selenium clients send WebDriver commands from an external process to a browser driver. Cypress test code executes in the browser run loop while a Node process handles privileged work, which enables snapshots, automatic retrying, and deep network control. Explain the tradeoff, not a winner: Cypress offers tight debugging and synchronization, while WebDriver supports more languages and conventional multi-window automation. The architectural difference affects debugging and supported workflows. Cypress can observe an application from inside its browser execution environment, whereas Selenium follows the standardized remote-control model. A team needing Java bindings or extensive native window handling may prefer WebDriver despite Cypress's richer in-runner diagnostics.

Q: What happens when Cypress executes a test?

The spec first queues Cypress commands during JavaScript evaluation. Cypress then executes that queue serially, waits for each command to complete, yields a subject to the next command, and records snapshots and logs. This model explains why a value from cy.get cannot be used synchronously and why mixing Cypress commands with uncontrolled asynchronous code causes ordering bugs. For example, a console.log placed after cy.get runs while commands are still being enqueued, not after the element appears. Put dependent work inside .then() or continue the Cypress chain. The Command Log reflects execution order and is the first place to verify an ordering assumption.

Q: Are Cypress commands promises?

No. Cypress commands are chainable objects placed in an internal queue, and they do not expose normal promise semantics for await. Cypress intentionally controls scheduling, retrying, timeouts, and subject passing. Use .then() to inspect a yielded value, return another Cypress chain from the callback, and use cy.wrap() when an actual application promise must join the queue. Consequently, Promise.all([cy.get(...), cy.get(...)]) is not a valid concurrency pattern. Return the chain from hooks and callbacks so Cypress knows when the work finishes. Native async work can be wrapped, but its result then enters Cypress's serial scheduling rather than creating parallel browser actions.

Q: What is a Cypress subject?

A subject is the value yielded by the previous command, such as a DOM collection, response, cookie, or plain object. Child commands consume an appropriate subject, while parent commands such as cy.visit() begin a new chain. Track what every command yields. If an assertion or callback changes the subject unexpectedly, start a new chain or return the intended value explicitly. Subject management matters after callbacks: returning a plain value from .then() changes what the next command receives, while returning undefined generally preserves the last Cypress command's yield. When a chain becomes difficult to read, alias a meaningful value or begin again with cy instead of relying on an accidental subject.

Q: What are Cypress query, assertion, and action commands?

Queries locate or derive state and can be retried, assertions express an expected condition and participate in that retry loop, and actions such as click perform an interaction once their actionability checks pass. The key distinction is that Cypress can rerun the linked query chain before an assertion, but it does not repeatedly click merely because a later assertion failed. In cy.get('button').should('be.enabled').click(), get and should may repeat, but click occurs once after the element becomes actionable. A later failed URL assertion starts from its own query chain. This distinction prevents the dangerous misconception that Cypress might submit a form repeatedly.

2. Command Queue, Retry-ability, and Assertions: Cypress Interview Questions and Answers

Q: What does retry-ability mean in Cypress?

Cypress automatically reruns linked queries and assertions until they pass or their timeout expires. A command like cy.get([data-cy=total]).should(have.text, $42) therefore waits for the observable state instead of sleeping. Describe it as state-based synchronization. The test becomes faster when the state arrives early and produces a meaningful timeout when the state never arrives. The timeout belongs to the query or assertion chain, with defaultCommandTimeout providing the normal ceiling. Retrying is safe because lookup and assertion work is observational. If a callback sends a request or mutates data, repeating it can create duplicates and should be moved to a one-time command.

cy.get(`[data-cy=total]`).should(`be.visible`).and(`have.text`, `$42`);

Q: Which Cypress commands retry?

Queries such as cy.get(), cy.contains(), and .find() retry when linked to assertions. Assertions retry with their preceding query chain, while non-query commands such as cy.visit(), cy.request(), and action commands execute once. Do not summarize this as every Cypress command retries. Interviewers often ask for the query versus non-query distinction. A useful diagnostic question is whether the failed line observes state or causes state. cy.get().find().should() can be reevaluated as a linked chain; cy.click() does not rerun because a second click could alter the product twice. cy.request() similarly sends one request per invocation.

Q: Why is cy.wait(2000) usually a bad practice?

A fixed sleep waits for an estimate rather than a condition. It wastes time when the application is fast and still fails when a slow run exceeds the estimate. Wait for an aliased request with cy.wait(@alias) or assert the visible state through a retrying query. Keep numeric waits only for rare demonstrations where elapsed time itself is the behavior under test. For a search result, alias GET **/search* before typing, wait for that alias if the response contract matters, then assert the result list. If only rendering matters, the list assertion alone may be enough. This ties the timeout to evidence instead of a guessed duration.

Q: What is actionability in Cypress?

Before an action such as click or type, Cypress checks that the element is attached, visible, enabled, not covered, and in an actionable position. It retries the query and checks until the timeout, then performs the action once. A forced click bypasses important checks and can hide a real usability defect. Diagnose overlays, animation, disabled state, or stale selection before considering force. Cypress also scrolls an element into view and checks animation distance before dispatching events at calculated coordinates. When a menu item is covered by a transition, fixing the application state or waiting for the menu's visible condition preserves fidelity. Increasing a timeout helps only if readiness is genuinely slow.

Q: How do .should() and .then() differ?

.should() is designed for assertions and its callback can be rerun while Cypress retries the preceding query. .then() runs once after the prior command resolves and is appropriate for transformations, branching, or one-time side effects. Never place an irreversible side effect inside a .should() callback. Use .should() for idempotent checks and .then() when repeat execution would be unsafe. A .should(($el) => expect($el.text()).to.include('Ready')) callback may execute several times. A .then(($el) => cy.request(...)) callback executes once and can safely perform that deliberate transition. Returning a Cypress chain from .then() delays downstream commands until that returned chain finishes.

cy.get(`[data-cy=row]`)
  .should(`have.length.at.least`, 1)
  .then(($rows) => expect($rows.length).to.be.greaterThan(0));

Q: How do you assert multiple properties without creating flake?

Keep assertions attached to a query that represents the state you need, and make every callback assertion idempotent. Cypress retries the query and the whole assertion callback, so related checks can observe one eventually consistent state. If properties arrive through separate events, use separate query chains or wait on the responsible network aliases rather than assuming all changes happen atomically. For one invoice card, a single callback can check amount, currency, and status because those fields represent one render. If status changes after a separate polling call, give it its own assertion. Grouping unrelated eventual states under one callback makes the last failing property obscure timing and ownership.

3. Selectors, DOM, and User Interactions: Cypress Interview Questions and Answers

Q: What selector strategy do you recommend for Cypress?

Prefer accessible roles and labels when they express user behavior, and stable data-cy attributes when the element has no reliable semantic selector. Avoid selectors tied to CSS layout, generated classes, or DOM depth. Agree on selector ownership with developers. A selector contract should survive visual refactoring but fail when user-facing semantics intentionally change. For example, select a Save button by accessible name if that label is product behavior; use [data-cy=invoice-row] for a structural row whose wording is localized. Data attributes should identify purpose, not duplicate styling names such as blue-button. Centralizing every selector can make simple tests harder to understand.

Q: What is the difference between cy.get() and cy.contains()?

cy.get() selects elements with a CSS selector or retrieves an alias. cy.contains() finds an element by visible text and can optionally narrow the element type or selector. Use text when the copy is part of the requirement, and use a stable test attribute when copy changes should not break the test. Scope both commands to the smallest meaningful container. cy.contains('button', 'Delete') combines element type and text, avoiding a match on explanatory copy elsewhere. Exact text can be supplied with a regular expression when substring matching is too broad. cy.get('@request') is also distinct from CSS lookup because the @ prefix resolves a Cypress alias.

Q: How do within() and find() differ?

.find() is a child query that searches descendants of the current subject and yields the matches. .within() scopes all Cypress queries inside its callback to a container but continues to yield the original subject. Use find for a linear chain and within for several related queries inside a form, row, dialog, or card. Avoid deeply nested scopes that obscure which container owns an assertion. Within a table row, .find('[data-cy=status]') naturally yields the status cell for further chaining. With .within(), a later cy.get is scoped during the callback, but attempts to change the callback's yielded subject are misleading. Re-query outside the callback when the next operation belongs to another region.

Q: How do you handle elements that detach after a re-render?

Do not hold a DOM element across an action that causes the framework to replace it. Re-query the element after the update and assert the new state through a stable selector. Cypress can retry queries, but it cannot make an old detached node become current. Treat detachment as a signal to align the chain with the application lifecycle. Split the chain at the mutation: click the control, then call cy.get with the same stable selector again. Aliasing a jQuery element before a React rerender preserves the obsolete node, not the logical component. Network completion can mark the transition, but the final DOM assertion proves the replacement is usable.

Q: When is force: true acceptable?

Force can be justified when the test intentionally invokes behavior that normal actionability prevents, such as a controlled hidden input owned by a custom widget. It should not be the default fix for a covered or disabled control. Document why a real user interaction is not being modeled, and separately test the visible widget behavior. Otherwise force removes useful evidence about the interface. A file input visually replaced by a styled label is a common controlled exception because the actual input may intentionally be hidden. Record that rationale beside the command. For ordinary buttons, a force option can make a test pass while customers remain unable to click through an overlay.

Q: How do you test a dropdown reliably?

A native example is cy.get('select[name=country]').select('IN').should('have.value', 'IN'). For an ARIA combobox, assert the expanded state, choose an option by name, and verify the resulting selection. Keyboard coverage is valuable when navigation and escape behavior are part of the widget contract.

4. Network Testing, APIs, and Stubbing: Cypress Interview Questions and Answers

Q: How does cy.intercept() work?

cy.intercept() matches browser network traffic by method, URL, route matcher, or callback. It can spy on a real exchange, provide a static response, mutate a request, or control a response before the application receives it. Register the intercept before the action that sends the request, give it an alias, trigger the behavior, then assert the interception object returned by cy.wait(). Route matchers can constrain method, URL, pathname, query, headers, or times. A handler may inspect and continue a real request, reply with a static response, or modify the outgoing and incoming phases. Register it before cy.visit or the triggering click, otherwise an early request can escape observation.

cy.intercept(`GET`, `/api/orders*`).as(`orders`);
cy.visit(`/orders`);
cy.wait(`@orders`).its(`response.statusCode`).should(`eq`, 200);
cy.get(`[data-cy=order-row]`).should(`have.length.at.least`, 1);

Q: What is the difference between spying and stubbing a request?

A spy observes the real request and response without replacing them. A stub returns a controlled response so the test can deterministically reach success, empty, slow, or failure states. Use spies for integrated confidence and stubs for focused UI behavior. A balanced suite keeps at least some paths against real services so contract drift is not hidden. A spy leaves the real server response intact and exposes the interception for assertions. A stub supplies a controlled reply, including status, body, delay, or network error. Use a spy for integration confidence and a stub for deterministic edge states that are costly or unsafe to create remotely.

Q: Why can cy.wait(@users) time out even though the request appeared?

The intercept may have been registered after the request, the method or URL matcher may be wrong, a cache or service worker may satisfy the call, or a previous wait may have consumed the expected occurrence. Inspect the command log and request URL, register earlier, narrow the matcher deliberately, and remove caching ambiguity. Do not increase the timeout until matching and ordering are proven. Frequent causes include registering the intercept after cy.visit, matching the wrong method or path, consuming the alias with an earlier wait, or serving the response from application code that never makes the expected call. Compare the browser network entry with the Route Matcher shown in the Command Log.

Q: How do you validate a request body and response?

Alias the intercept, wait for it, and assert interception.request.body, response.statusCode, headers, and selected response fields. Assert only contract-relevant details rather than duplicating every payload property. Pair transport assertions with a user-visible outcome. That proves both that the application sent the right intent and that it rendered the resulting state. cy.wait('@createUser').then(({ request, response }) => { ... }) exposes both sides of the exchange. Assert only fields owned by the scenario, such as request.body.email, response.statusCode, and a returned id. Duplicating the entire schema in an E2E test creates brittle coverage better placed in API contract tests.

Q: When should you use cy.request()?

Use cy.request() for fast API-assisted setup, cleanup, authentication, and direct HTTP assertions where browser rendering is irrelevant. It executes through Cypress rather than through the application page and yields a response object. Do not use it as a substitute for every UI journey. Keep a small number of tests that prove the browser actually constructs and handles the request. Use it for authenticated setup, teardown, health checks, and API behavior where no rendering risk exists. It bypasses the browser UI and does not prove that frontend code issued a request. Keep one visible journey for the integration, then use requests to create prerequisite records efficiently.

Q: How do you test GraphQL operations with cy.intercept()?

Match the GraphQL endpoint, inspect request.body.operationName in a route handler, and assign an alias for the operation under test. Then wait for that alias and assert variables, errors, and the UI outcome. Because many operations share one URL, matching only the path is too broad. Operation names and selected variables create a stable intent-level matcher. GraphQL often shares one POST endpoint, so URL matching alone cannot distinguish operations. Inspect req.body.operationName in the route handler, assign req.alias for the desired operation, and wait on that alias. Persisted queries may require matching a hash or request variables instead of readable query text.

cy.intercept(`POST`, `/graphql`, (req) => {
  if (req.body.operationName === `GetOrders`) req.alias = `getOrders`;
});
cy.visit(`/orders`);
cy.wait(`@getOrders`).its(`response.statusCode`).should(`eq`, 200);

5. Test Data, Fixtures, Authentication, and Sessions: Cypress Interview Questions and Answers

Q: What are fixtures, and when should you avoid them?

Fixtures are static files loaded with cy.fixture() or referenced by a stub response. They are useful for stable representative payloads but become harmful when giant snapshots obscure which fields matter or drift from production contracts. Prefer small builders for scenario-specific data and validate shared fixtures against schemas when possible. A fixture should communicate the state being tested, not act as an unreviewed database dump. Fixtures are versioned static files loaded with cy.fixture or referenced by a stub response. They suit stable payloads such as a known error shape. Avoid them for records that must be unique, reflect a changing backend schema, or encode mutable shared state; factories make ownership clearer.

Q: How does cy.session() improve authentication?

cy.session() caches and restores cookies, local storage, and session storage created by a setup function. A validation callback confirms that restored state is still accepted before the test proceeds. Key sessions by all identity inputs such as role and tenant. Keep one direct UI login test, while most specs use approved programmatic login inside the session setup. cy.session(id, setup, options) snapshots cookies plus local and session storage after setup, then restores them for matching ids. The id must include every value that changes the authenticated state, such as role or tenant. A validate callback should cheaply prove the restored identity still works.

Q: What does cacheAcrossSpecs do?

With cacheAcrossSpecs enabled, a named session can be reused by later specs on the same Cypress machine during a run. It does not distribute a browser session across separate CI workers. Every worker must be able to establish its own valid session. Shared backend accounts still need isolation if tests mutate user state. With cacheAcrossSpecs: true, a session created in one spec can be reused by later specs in the same Cypress run on that machine. It does not share state across independent CI containers. Setup must therefore remain able to recreate the session on every shard and after cache invalidation.

Q: How do you keep tests independent?

Each test creates or owns its prerequisites, performs one coherent scenario, and leaves no required state for a later test. Use APIs or tasks for deterministic setup and idempotent cleanup. Prove independence by running one test alone, changing spec order, and executing specs in parallel. A before hook that creates shared mutable state is a common hidden dependency. Create owned records in beforeEach or inside the test, use unique identities where mutations occur, and clean up through an API when necessary. Never require spec B to consume data produced by spec A because order and machine placement can change. Test isolation resets browser context, not external database state.

Q: How should secrets and environment values be handled?

Keep non-secret configuration in versioned config or CI variables and inject secrets from the CI secret store. Read Cypress environment values through Cypress.env() without printing credentials, tokens, or sensitive request bodies. Remember that browser-visible values are not secret once delivered to client code. Use Node tasks or backend test-support APIs for privileged operations. Store credentials in the CI secret manager or local untracked environment, then expose only necessary values through Cypress configuration. Values sent to browser-side test code can appear in logs or artifacts. Privileged operations belong in Node tasks or protected test-support endpoints with carefully masked output.

Q: How do you create unique test data in parallel runs?

Build an ownership key from the CI run, worker, test purpose, and a safe unique suffix. Allocate separate users or tenants for workflows that mutate shared state, and clean only records owned by that run. Random names alone prevent collisions but make cleanup and diagnosis harder. Traceable namespaces provide both uniqueness and operational accountability. Combine a run identifier, worker or machine index, and per-test suffix in emails and record keys. Prefer server-side factories that return the created id, then delete by id. Random strings alone reduce collisions but make a failed record difficult to trace back to its run and owner.

6. Configuration, Custom Commands, Plugins, and TypeScript: Cypress Interview Questions and Answers

Q: What belongs in cypress.config.ts?

The config defines testing types, base URLs, spec patterns, retries, timeouts, reporters, browser behavior, environment defaults, and the setupNodeEvents callback. Use defineConfig() for typing and keep secrets outside the committed file. Prefer explicit testing-type configuration because component and end-to-end suites have different servers, support files, and risks. Review overrides from CLI and CI as part of debugging. Keep runner-wide policy there: e2e and component blocks, baseUrl, retries, viewport, timeouts, spec patterns, reporters, and setupNodeEvents registration. Business workflows and selectors do not belong in configuration. Return the final config from setupNodeEvents when plugins or environment loading modify it.

Q: When should you create a custom command?

Create a custom command when a domain action is widely reused and naturally participates in Cypress subject chaining, logging, and retry behavior. Use a plain function for pure transformations or helpers that do not need Cypress command semantics. Keep commands few, typed, and intention-revealing, such as loginAsRole. A command that merely renames click or hides ten unrelated steps makes failures harder to locate. Create one for a repeated Cypress-native capability with stable semantics, such as loginAs or seedOrder, not merely to shorten three readable lines. Preserve chaining behavior and choose parent, child, or dual prevSubject deliberately. Domain helper functions are often easier to type, compose, and unit test.

Q: How do you type custom commands in TypeScript?

Add the implementation with Cypress.Commands.add() and augment the Cypress.Chainable interface in a declaration file included by tsconfig. Ensure the declared subject and yielded return type match the actual command. Run TypeScript checking independently of Cypress execution. Incorrect declarations can make editor completion look safe while runtime subject behavior is different. Augment Cypress.Chainable in a declaration file included by tsconfig, and make the declared signature match Cypress.Commands.add. For a child command, type the prevSubject and return value as well as arguments. A mismatch can compile at call sites yet conceal an incorrect runtime subject assumption.

Q: What is cy.task(), and what must it return?

cy.task() invokes a named handler registered in setupNodeEvents and bridges from the browser test to the Node process. It is appropriate for database helpers, filesystem work, or approved external integrations. A task must resolve to a serializable value other than undefined, and returning null is valid when no data is needed. Keep handlers deterministic and never expose privileged secrets in logs. cy.task sends serializable data from browser-side test code to a handler registered in setupNodeEvents. It is appropriate for database setup, filesystem inspection, or other Node-only work. The handler must resolve to a value other than undefined; return null when no payload is needed.

import { defineConfig } from `cypress`;
export default defineConfig({ e2e: { setupNodeEvents(on) {
  on(`task`, { seedUser: ({ role }: { role: string }) => Promise.resolve({ id: `user-1`, role }) });
} } });

Q: How do you use environment-specific base URLs?

Set baseUrl through configuration or a controlled environment override and visit relative paths in tests. Keep environment selection in the run command or CI job rather than branching throughout spec code. The same test should express the same behavior everywhere. Differences in capabilities or seeded data should be explicit test-environment contracts. Define a safe default in configuration and override it through CYPRESS_BASE_URL or the CLI for each deployment. Tests should visit relative paths so the target changes without code edits. Validate the intended host early in CI to prevent destructive suites from reaching production by a mistaken variable.

Q: How would you organize a scalable Cypress repository?

Organize specs by business capability, keep support commands thin, place data builders and API clients in focused modules, and separate component from E2E configuration. Tests should read as behavior while implementation helpers remain discoverable. Group specs by business capability, colocate narrow helpers with their feature, and keep shared support limited to genuine cross-cutting behavior. Separate E2E and component configuration where their runtime needs differ. Factories, API clients, and typed domain commands should expose ownership instead of forming one universal utilities folder.

7. Component Testing and Test Boundaries: Cypress Interview Questions and Answers

Q: What is Cypress component testing?

Component testing mounts an individual UI component in a real browser through the framework adapter and cy.mount(). It offers Cypress interaction, network control, and debugging without starting the complete application journey. Use it for rendering variants, user interaction, accessibility states, and edge cases that are expensive to arrange through E2E setup. Component testing mounts a component in a real browser with a development bundler, then uses the familiar Cypress command and assertion model. It exercises rendering, events, styles, and browser APIs without deploying the whole application. Framework adapters provide mount support for React, Vue, Angular, and other supported ecosystems.

Q: When should a test be component rather than E2E?

Choose component scope when the risk belongs to one component and dependencies can be supplied through props, providers, or network boundaries. Choose E2E when confidence depends on routing, authentication, deployed integration, or several services working together. The smallest realistic boundary usually gives the clearest failure. Do not mock so much that the behavior under test no longer resembles production. Choose component scope when the risk is owned by one component and its immediate collaborators, such as validation states, keyboard behavior, conditional rendering, or emitted events. Choose E2E when routing, authentication, deployed configuration, or multiple services are the subject. Duplicate only a few critical seams intentionally.

Q: How do you mount a React component in Cypress?

Install and configure the Cypress React adapter, define a typed custom mount command that calls the adapter mount, and wrap the component in the same required providers used by the application. Then call cy.mount() and interact through the rendered DOM. Centralize only universal providers. Let each test declare scenario-specific routes, state, and props so its prerequisites remain visible. Configure the React component dev server in the component block, register a cy.mount command using the Cypress React adapter, and mount JSX inside the spec. Wrap the component with the same providers it requires in production. A reusable mountWithProviders helper can supply routing, theme, and store defaults.

Q: Can cy.intercept() be used in component tests?

Yes, because mounted components run in a browser and can make HTTP requests. Register the intercept before mounting or before the user action that triggers the request, then assert the alias and rendered outcome. If the component receives data entirely through props, prefer direct props. Intercept only when network behavior is genuinely part of the component boundary. Yes, provided the mounted component issues browser network requests that Cypress can observe. Register the route before mounting or before the triggering interaction, then assert loading, success, empty, and error states. If the component receives data only through props, control those props instead of inventing a network boundary.

Q: How do you test component events or callbacks?

Pass a Cypress stub as the callback prop, perform the user action, and assert the stub was called with the required arguments. Also assert visible state when the component owns a UI change. A callback assertion proves the component contract without requiring a full parent application. Name the stub so command-log failures explain the expected event. Pass cy.stub().as('onSave') as the callback prop, perform the user action, and assert the alias was called with the expected payload. This verifies the component's outward contract without mounting an entire page. Avoid asserting private state variables when the event and rendered result already describe behavior.

Q: What are common component testing mistakes?

Common mistakes include rebuilding the whole application in the mount helper, globally hiding providers, asserting implementation details, and treating mocked component tests as proof of deployment integration. Keep mounts minimal and behavior-focused, then preserve a smaller E2E layer for wiring. The Cypress component testing guide and component testing examples show both boundaries. Over-mocking makes the component pass in a world unlike the application, while under-provisioning required context produces failures unrelated to behavior. Other traps are testing implementation state, omitting responsive or accessibility states, and treating component success as proof that routing, backend contracts, and deployment integration work.

8. Retries, Flake, Debugging, and CI: Cypress Interview Questions and Answers

Q: How do Cypress test retries work?

Configured test retries rerun a failed test attempt in run mode or open mode according to project settings. They are different from command retry-ability, which repeatedly queries within one test attempt. Retain evidence from every attempt and track tests that pass only after retry. A retry reduces pipeline noise but does not remove the defect that caused nondeterminism. Runner retries start the complete test again after a failed attempt, subject to runMode and openMode settings. Hooks execute according to Cypress retry behavior, so setup must be repeatable. Preserve artifacts from each attempt and treat a later pass as a flake signal, not as an ordinary green result.

Q: How do you diagnose a flaky Cypress test?

Start with the first failed attempt, command log, screenshot, video, network evidence, and application logs tied to a test identifier. Reproduce the spec alone and under suite concurrency, then classify the cause as synchronization, data, environment, product race, or test bug. Change one cause at a time and prove stability across repeated runs. The Cypress flaky test guide provides a practical triage workflow. Begin with the first failed attempt and classify the missing event: selector instability, late intercept, uncontrolled data, stale subject, animation, server variance, or resource pressure. Reproduce with video, screenshots, command logs, and request evidence. Change one cause at a time and verify retry-only passes decline.

Q: What should run in CI for Cypress?

Install from the lockfile, build and start the application, verify readiness, run Cypress non-interactively, preserve exit status, and upload useful artifacts even on failure. Pin supported runtime and browser inputs according to the repository maintenance policy. Separate fast pull-request feedback from broader nightly or release coverage. Every required job needs a clear ownership and quarantine policy. Start the application deterministically, wait on a health endpoint, run Cypress with a pinned browser and dependency lockfile, and preserve the exit code. Split specs across workers when useful. Upload screenshots, videos, reports, and relevant application logs under unique run and attempt identifiers.

Q: How does Cypress Cloud parallelization work?

Multiple CI machines join one recorded run and group, then Cypress Cloud assigns whole spec files using recorded duration history. Workers must share the build identity and discover the same spec set. One oversized spec remains an indivisible critical path. Combine dynamic orchestration with isolated data and coherent spec boundaries. Recorded machines join one run and Cypress Cloud distributes spec files using historical duration data to reduce total wall time. Parallel workers need the same CI build identifier and compatible configuration. Cloud coordination does not isolate backend records, so data ownership and per-worker identities remain suite responsibilities.

Q: How do you debug a test that passes locally but fails in CI?

Reproduce CI inputs: headless browser, viewport, environment variables, server command, data, timezone, resources, and exact dependency lockfile. Compare videos and logs, and run the same container or CI command locally when available. Do not label it a CI issue before identifying the state difference. Resource pressure may expose a real race that a fast laptop hides. Align browser version, viewport, timezone, locale, environment variables, CPU constraints, and application build first. Run the same headless command locally or in the container image. CI-only failures often reveal implicit timing or environmental assumptions; arbitrary timeout increases merely make that evidence slower.

Q: How should screenshots, videos, and logs be managed?

Capture enough evidence to explain the first failure, name artifacts uniquely by worker and attempt, and upload them with an always condition while preserving the test exit code. Apply retention and access controls because artifacts may contain user or environment data. Prefer targeted application and network logs over indiscriminate secret-bearing output. Evidence should shorten diagnosis without creating a security problem. Capture on failure by default, retain enough attempts to diagnose intermittent behavior, and apply a policy for storage duration and sensitive data. Name artifacts with spec, test, shard, and attempt. Redact tokens and personal data before upload, and ensure artifact failures never replace the test process's exit status.

9. Cross-Origin, Browser Constraints, and Tool Choice: Cypress Interview Questions and Answers

Q: How does cy.origin() handle cross-origin flows?

cy.origin() runs a callback in the secondary origin so Cypress can safely interact after navigation across origins. Values passed through args must be serializable, and commands inside the callback execute in that origin context. Use it for necessary identity or partner flows, but keep third-party coverage focused. Stub or bypass external systems in most tests when their availability is not the product risk. cy.origin(targetOrigin, args, callback) executes Cypress commands inside the secondary origin's context. Values passed through args must be serializable, and closures from the outer callback are unavailable. Place only the cross-origin interaction inside it, then return to assertions that belong to the primary application.

cy.origin(`https://id.example.test`, { args: { username } }, ({ username }) => {
  cy.get(`input[name=email]`).type(username);
  cy.get(`button[type=submit]`).click();
});

Q: Can Cypress control multiple browser tabs?

Cypress does not offer general native multi-tab automation in the same style as tools with page or window objects. Common tests assert the target URL, remove the target attribute under controlled conditions, or visit the destination directly. If simultaneous multi-page control is a central requirement, acknowledge that tool fit matters. Do not disguise a product requirement as a selector workaround. Cypress centers a test on one active browser tab and does not provide Playwright-style Page objects for simultaneous tabs. For target=_blank links, assert the href or remove the target and visit in the same tab when that preserves the risk. Choose another tool for true concurrent-window workflows.

Q: How do you test file downloads?

Trigger the download, verify the response or browser behavior, and use a Node task or filesystem assertion when the downloaded bytes must be inspected. Trigger the download, then verify the expected file under downloadsFolder with cy.readFile or a Node task when binary inspection is required. Assert the response headers through cy.request when UI behavior is not the risk. Clean or uniquely name files so parallel runs cannot read stale output.

Q: How do Cypress and Playwright differ at the command-model level?

Cypress queues chainable commands and automatically retries linked queries and assertions. Playwright uses async and await with locator actions and web-first assertions, and its browser-context model directly represents pages, popups, and multiple isolated contexts. Compare architecture and requirements rather than syntax alone. Cypress can be excellent for in-browser debugging and component tests, while Playwright often fits multi-page and multi-context workflows naturally. Cypress enqueues chainable commands and retries linked queries under its scheduler. Playwright exposes promise-based async APIs and auto-waiting locators, with explicit browser contexts and pages. Translating syntax without changing the mental model leads to errors such as awaiting Cypress commands or forgetting await in Playwright.

Q: When would you choose Cypress over Playwright?

Choose Cypress when its interactive runner, component testing workflow, browser-level instrumentation, team JavaScript skills, and existing ecosystem provide the best delivery path. Validate required browsers, origins, tabs, CI topology, and debugging needs with a proof of concept. Avoid claiming universal superiority. A senior recommendation maps product risks and team constraints to a tool and includes migration and maintenance cost. Cypress is compelling when a JavaScript team values an interactive command timeline, DOM snapshots, component testing in the same workflow, and tight application instrumentation. The decision changes if requirements demand several simultaneous pages, broader language bindings, or browser-context orchestration. Prototype the hardest scenario before standardizing.

Q: What limitations should a senior Cypress engineer state openly?

State the JavaScript and TypeScript focus, browser sandbox constraints, different multi-tab model, cross-origin ceremony, and the operational needs of large parallel suites. Also explain the supported workaround and the point at which another tool is a better fit. Credibility comes from naming tradeoffs without dismissing Cypress strengths. Interviewers want engineering judgment, not product advocacy. A senior answer mentions the single-tab-centered model, JavaScript and TypeScript focus, cross-origin callback constraints, and the need to design around Cypress's controlled queue. It should also distinguish current limitations from obsolete claims. cy.origin and modern component testing removed several historical constraints but not every workflow mismatch.

10. Scenario Design, Leadership, and Framework Evolution: Cypress Interview Questions and Answers

Q: How would you migrate a brittle Cypress suite?

Measure failure categories and runtime first, then stabilize selectors, state setup, authentication, and network synchronization before reorganizing folders. Move repeated domain setup into typed helpers only after behavior is understood. Migrate in slices with visible success criteria such as fewer unexplained retries, independent specs, and faster diagnosis. A rewrite without evidence can reproduce the same architecture under new names. Inventory failures and business risks before rewriting. Stabilize selectors, data creation, intercept timing, and authentication in the highest-value specs, then split oversized journeys across API, component, and focused E2E layers. Migrate incrementally with measurable reductions in duration, retry-only passes, and diagnosis time.

Q: How do you review a Cypress pull request?

Review the risk covered, test boundary, independence, selector contract, synchronization, negative paths, data ownership, assertion strength, and diagnostic output. Run the changed test alone and consider how it behaves under retries and parallel workers. Reject fixed sleeps, forced actions without reasons, and helpers that hide the business intent. Also avoid demanding abstractions before duplication demonstrates a stable pattern. Read the risk statement first, then inspect isolation, selector contracts, command ordering, network synchronization, assertion ownership, and failure evidence. Reject sleeps, unconditional force, hidden global state, and shared mutable identities unless justified. Run the changed spec repeatedly and in its normal CI topology when flake risk is meaningful.

Q: How do you decide what not to automate in Cypress?

Do not put a behavior in Cypress when a lower layer provides equal confidence faster and with clearer failures, or when the scenario depends on uncontrolled third parties with little product value. Keep exploratory, visual-judgment, and rapidly changing low-risk checks manual when automation cost exceeds feedback value. Record the risk decision rather than chasing an automation percentage. Cypress coverage should protect valuable user outcomes. A payment provider's internal UI, one-off data repair, or subjective visual judgment may provide poor Cypress return. Cover the contract at the boundary you own and retain targeted manual exploration. Revisit the choice when frequency, impact, product stability, or available lower-level hooks change.

Q: How would you test a multi-user workflow?

Give each actor an isolated identity, create owned data through APIs, and switch roles through separate sessions while validating backend state between browser actions. Cypress test isolation means a single test may orchestrate roles, but the design must not depend on another spec. For approval, create an item as requester via API, restore an approver session to act in the UI, then verify requester-visible state through a fresh session. This models sequential roles. If the requirement is live collaborative editing across two pages, Cypress's model may be less suitable than multi-context automation.

Q: How do you measure Cypress suite health?

Track wall-clock feedback time, failure classification, retry-only passes, quarantine age, critical-path specs, setup cost, and time to diagnose. Raw test count and pass percentage can look healthy while unstable retries consume trust. Review trends by business capability and owner. Use metrics to prioritize architecture work, not to punish teams for surfacing defects. Useful ratios include retry-only passes per run, quarantined-test age, and failures with enough evidence for immediate ownership. Segment wall time into application startup, test setup, and execution. A falling median can still hide a severe long-tail shard, so inspect distribution and the slowest critical specs.

Q: What would a strong first 90-day Cypress improvement plan include?

Baseline risks and failure data, document supported test boundaries, repair the highest-cost flake sources, establish selector and data contracts, and make CI evidence reliable. Then improve component coverage, parallel balance, and developer coaching in measured increments. Tie each change to product feedback and team ownership. A framework succeeds when engineers can add and diagnose tests without a specialist becoming a bottleneck. Days 1 through 30 establish a failure taxonomy and baseline. Days 31 through 60 repair data, selector, and synchronization contracts in critical paths. Days 61 through 90 add lower-layer coverage, balance CI shards, publish ownership standards, and compare the new metrics with the baseline.

11. Practical Cypress Coding Questions and Answers

Q: Show a reliable test that waits for a request and asserts the UI.

Register the intercept before the behavior, wait for the named request, assert its contract, and then assert the user-visible state. This example uses current TypeScript command APIs and stable selectors.

describe('profile update', () => {
  it('saves a display name', () => {
    cy.intercept('PUT', '**/api/profile').as('updateProfile')
    cy.visit('/profile')
    cy.get('[data-cy=display-name]').clear().type('Asha Rao')
    cy.get('[data-cy=save-profile]').click()
    cy.wait('@updateProfile').then(({ request, response }) => {
      expect(request.body).to.include({ displayName: 'Asha Rao' })
      expect(response?.statusCode).to.eq(200)
    })
    cy.get('[data-cy=success-message]').should('be.visible').and('contain.text', 'Saved')
  })
})

The network assertion identifies the request Cypress observed, while the DOM assertion proves the application rendered success. There is no numeric sleep, and a failure distinguishes transport from presentation.

Q: Show a typed reusable login session.

Use a domain helper with a session key that includes identity and a validation request. The setup performs approved API authentication and the validation prevents reuse of expired state.

type Role = 'admin' | 'viewer'

export function loginAs(role: Role, email: string) {
  cy.session(['login', role, email], () => {
    cy.request('POST', '/test-support/login', { role, email })
      .its('status')
      .should('eq', 204)
  }, {
    validate() {
      cy.request<{ role: Role; email: string }>('/api/me')
        .its('body')
        .should('include', { role, email })
    },
    cacheAcrossSpecs: true,
  })
}

Do not put a shared mutable user behind this helper when tests change permissions or profile data. Allocate an identity per worker or scenario, and retain one dedicated UI login test.

Q: Show retry configuration and explain what it does.

import { defineConfig } from 'cypress'

export default defineConfig({
  retries: { runMode: 2, openMode: 0 },
  e2e: {
    baseUrl: 'http://127.0.0.1:4173',
    setupNodeEvents(on, config) {
      return config
    },
  },
})

This permits up to two additional attempts during non-interactive runs and none during open mode. It does not change command retry-ability and must not be presented as a flake fix. Preserve all attempts, classify retry-only passes, and remove the cause.

Q: Show a component test for a callback.

import { SaveButton } from './SaveButton'

describe('<SaveButton />', () => {
  it('reports the save intent', () => {
    const onSave = cy.stub().as('onSave')
    cy.mount(<SaveButton label="Save profile" onSave={onSave} />)
    cy.findByRole('button', { name: 'Save profile' }).click()
    cy.get('@onSave').should('have.been.calledOnce')
  })
})

This assumes the repository has registered cy.mount and installed the Testing Library queries. If it has not, select the button with cy.contains('button', 'Save profile') or a stable test attribute rather than inventing an unavailable command.

How Interviewers Grade Your Answers

Interviewers usually grade at four levels. A weak answer names a command. A developing answer describes syntax and a happy path. A strong answer explains execution mechanics, gives a realistic example, and names a failure mode. A senior answer also chooses the correct test boundary, addresses parallel data and CI evidence, and states when the tool is a poor fit.

Signal What earns credit What loses credit
Mental model Queue, subjects, queries, actions, Node boundary Calling commands promises or saying everything retries
Reliability Observable waits, isolation, owned data Fixed sleeps, shared mutable users, force by default
Test design Smallest realistic boundary and risk-based coverage Turning every scenario into a long E2E flow
Debugging First-attempt evidence and cause classification Raising timeouts without diagnosing the event
Architecture Typed helpers with visible intent Giant page objects and hidden global setup
Judgment Honest Cypress versus Playwright tradeoffs Tool advocacy without requirements

For coding questions, narrate ordering before typing. Say what request or state change you will synchronize with, which selectors are contracts, and what output proves success. For design questions, clarify scale, browsers, origins, data ownership, and CI topology before proposing a framework.

Common Mistakes

  • Saying Cypress commands are promises or using await on cy.get().
  • Claiming all commands retry, including click and request.
  • Registering cy.intercept() after the application already sent the request.
  • Replacing every synchronization problem with cy.wait(number).
  • Using force: true to bypass a covered, disabled, or detached element.
  • Sharing one mutable user or tenant across parallel workers.
  • Treating test retries as proof that a flaky test is repaired.
  • Hiding business behavior behind giant page objects or custom commands.
  • Using component mocks as the only proof that deployed integration works.
  • Recommending Cypress or Playwright without asking about pages, origins, languages, browsers, and team constraints.
  • Uploading artifacts under one shared name or allowing artifact steps to swallow the Cypress exit code.
  • Quoting old limitations without mentioning cy.origin(), modern component testing, and current browser support.

Keep Practicing

Start with the modern Cypress test architecture complete guide, then build a working suite with Cypress framework from scratch. Deepen the core mechanics through Cypress retry-ability, cy.intercept network control, cy.session authentication, and Cypress component testing.

For role-specific rounds, continue with Cypress scenario-based interview questions and the focused Cypress interview questions for 5 years experience. Use the QAJobFit practice surface at /dashboard to rehearse two-minute spoken answers, then repeat each answer with one code example and one tradeoff.

The best preparation is to run the examples in a small repository, deliberately create a late request, detached element, expired session, and parallel data collision, then diagnose each failure from evidence. That experience turns memorized Cypress interview questions and answers into engineering judgment you can defend.

Interview Questions and Answers

What is Cypress, and where does it fit in a test strategy?

Cypress is a JavaScript and TypeScript testing platform for browser-based end-to-end and component tests. It runs a Node process alongside browser code and gives tests direct, instrumented access to the application, DOM, network, storage, and timers. Position Cypress as one layer: keep business logic in unit tests, components in component tests, a focused set of user journeys in E2E tests, and API checks where the browser adds no value.

How does Cypress architecture differ from Selenium?

Selenium clients send WebDriver commands from an external process to a browser driver. Cypress test code executes in the browser run loop while a Node process handles privileged work, which enables snapshots, automatic retrying, and deep network control. Explain the tradeoff, not a winner: Cypress offers tight debugging and synchronization, while WebDriver supports more languages and conventional multi-window automation.

What happens when Cypress executes a test?

The spec first queues Cypress commands during JavaScript evaluation. Cypress then executes that queue serially, waits for each command to complete, yields a subject to the next command, and records snapshots and logs. This model explains why a value from cy.get cannot be used synchronously and why mixing Cypress commands with uncontrolled asynchronous code causes ordering bugs.

Are Cypress commands promises?

No. Cypress commands are chainable objects placed in an internal queue, and they do not expose normal promise semantics for await. Cypress intentionally controls scheduling, retrying, timeouts, and subject passing. Use .then() to inspect a yielded value, return another Cypress chain from the callback, and use cy.wrap() when an actual application promise must join the queue.

What is a Cypress subject?

A subject is the value yielded by the previous command, such as a DOM collection, response, cookie, or plain object. Child commands consume an appropriate subject, while parent commands such as cy.visit() begin a new chain. Track what every command yields. If an assertion or callback changes the subject unexpectedly, start a new chain or return the intended value explicitly.

What are Cypress query, assertion, and action commands?

Queries locate or derive state and can be retried, assertions express an expected condition and participate in that retry loop, and actions such as click perform an interaction once their actionability checks pass. The key distinction is that Cypress can rerun the linked query chain before an assertion, but it does not repeatedly click merely because a later assertion failed.

What does retry-ability mean in Cypress?

Cypress automatically reruns linked queries and assertions until they pass or their timeout expires. A command like cy.get(`[data-cy=total]`).should(`have.text`, `$42`) therefore waits for the observable state instead of sleeping. Describe it as state-based synchronization. The test becomes faster when the state arrives early and produces a meaningful timeout when the state never arrives.

Which Cypress commands retry?

Queries such as cy.get(), cy.contains(), and .find() retry when linked to assertions. Assertions retry with their preceding query chain, while non-query commands such as cy.visit(), cy.request(), and action commands execute once. Do not summarize this as every Cypress command retries. Interviewers often ask for the query versus non-query distinction.

Why is cy.wait(2000) usually a bad practice?

A fixed sleep waits for an estimate rather than a condition. It wastes time when the application is fast and still fails when a slow run exceeds the estimate. Wait for an aliased request with cy.wait(`@alias`) or assert the visible state through a retrying query. Keep numeric waits only for rare demonstrations where elapsed time itself is the behavior under test.

What is actionability in Cypress?

Before an action such as click or type, Cypress checks that the element is attached, visible, enabled, not covered, and in an actionable position. It retries the query and checks until the timeout, then performs the action once. A forced click bypasses important checks and can hide a real usability defect. Diagnose overlays, animation, disabled state, or stale selection before considering force.

How do .should() and .then() differ?

.should() is designed for assertions and its callback can be rerun while Cypress retries the preceding query. .then() runs once after the prior command resolves and is appropriate for transformations, branching, or one-time side effects. Never place an irreversible side effect inside a .should() callback. Use .should() for idempotent checks and .then() when repeat execution would be unsafe.

How do you assert multiple properties without creating flake?

Keep assertions attached to a query that represents the state you need, and make every callback assertion idempotent. Cypress retries the query and the whole assertion callback, so related checks can observe one eventually consistent state. If properties arrive through separate events, use separate query chains or wait on the responsible network aliases rather than assuming all changes happen atomically.

What selector strategy do you recommend for Cypress?

Prefer accessible roles and labels when they express user behavior, and stable data-cy attributes when the element has no reliable semantic selector. Avoid selectors tied to CSS layout, generated classes, or DOM depth. Agree on selector ownership with developers. A selector contract should survive visual refactoring but fail when user-facing semantics intentionally change.

What is the difference between cy.get() and cy.contains()?

cy.get() selects elements with a CSS selector or retrieves an alias. cy.contains() finds an element by visible text and can optionally narrow the element type or selector. Use text when the copy is part of the requirement, and use a stable test attribute when copy changes should not break the test. Scope both commands to the smallest meaningful container.

Frequently Asked Questions

Are Cypress commands promises?

No. Cypress commands are queued chainables controlled by the Cypress scheduler. Use .then() for yielded values and cy.wrap() for an external promise.

What should I study for a Cypress interview?

Prioritize architecture, command queuing, retry-ability, selectors, cy.intercept(), cy.session(), component testing, isolation, CI, and debugging. Practice explaining tradeoffs and failure evidence, not only command syntax.

How many Cypress interview questions are in this guide?

The numbered topic sections contain 60 core questions, followed by four practical coding questions. They span beginner, intermediate, advanced, and leadership expectations.

How is command retry-ability different from test retries?

Command retry-ability reruns linked queries and assertions inside one attempt until a timeout. Test retries start the failed test again as a new attempt according to configuration.

Should I use page objects with Cypress?

Use domain-focused helpers when they improve intent, but avoid large stateful page objects that hide Cypress chains and assertions. Cypress works well with small functions, typed commands, and component or screen objects with narrow responsibilities.

Is Cypress or Playwright better for interviews?

Neither is universally better. Cypress emphasizes queued chains, browser instrumentation, and component workflows, while Playwright uses async APIs and first-class pages and contexts. Choose from product and team requirements.

Can Cypress test APIs without opening a page?

Yes. cy.request() can call HTTP endpoints for direct assertions or test setup without visiting a page. Keep browser tests for risks involving rendering and user interaction.

Related Guides