ScreenshotNeo

BlogHow-to

How to Use Cypress for End-to-End Testing

Install Cypress, write a user-focused end-to-end test, run it locally, and add reliable CI execution with a ready-to-use workflow.

By the ScreenshotNeo team4 October 20269 min read

Cypress end-to-end (E2E) tests exercise your application in a browser, following user-like journeys across the running app. To get started, install Cypress as a development dependency, open the Cypress app and choose End-to-End Testing, start your application, write a spec that performs an action and asserts the resulting state, then run it with cypress open locally or cypress run in automation. In CI, wait for the application to become ready before launching Cypress.

1. Choose the right Cypress test type

Use E2E tests for important journeys that cross the browser, application, and services: signing in, completing checkout, creating a record, or navigating between pages. A useful test proves an outcome after an action. A script that only visits a page and clicks controls without checking the result provides little protection against regressions.

Component testing mounts a component in isolation; E2E testing runs the app as a user encounters it. E2E tests therefore need a running application and may involve more setup and dependencies. Reserve them for critical flows, and cover smaller component behavior at the component level when that is a better fit. See Cypress’s guides to why Cypress and effective E2E testing.

2. Install Cypress

Run the command for the package manager your project already uses from its root directory:

# npm
npm install --save-dev cypress

# Yarn
yarn add --dev cypress

# pnpm
pnpm add --save-dev cypress

# Bun
bun add --dev cypress

Consult the official installation guide for current system requirements, supported platforms, and any required Linux libraries. Installation behavior can vary by npm version: the current guide documents an allowScripts change affecting postinstall scripts in newer npm releases. If Cypress does not download or initialize as expected, check that guidance for your installed npm version rather than assuming the package install alone completed setup.

The guide currently lists Chrome, Edge, and Firefox support for their latest three major versions, WebKit as experimental, and Electron as deprecated as a test browser. Browser support changes, so verify the current support matrix before choosing a CI browser.

3. Initialize E2E testing

From the project root, launch the Cypress app:

npx cypress open

On the first launch, choose End-to-End Testing. Cypress Launchpad creates the initial configuration and project structure, including the folders and example specs. Review the generated configuration and keep it in the repository so local development and CI use the same settings. See Open the Cypress app for the current setup flow.

For a basic project, the generated cypress.config.js can remain mostly as created. Set the app’s base URL so specs can use relative paths:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
  },
})

Use the port your development server actually listens on. If your project uses ES modules or TypeScript, retain the config format Launchpad generated and make the same e2e.baseUrl setting in that file.

4. Start the app and write your first spec

Start the app in a separate terminal using the project’s normal development command. For example:

npm run dev

Wait until the server is ready, then add a spec under cypress/e2e/. This example assumes the application has a link labeled “Get started” that navigates to /signup, where a page heading says “Create your account.” Adapt the link text, route, and heading to your app:

describe('sign-up navigation', () => {
  it('opens the sign-up page from the home page', () => {
    cy.visit('/')
    cy.contains('a', 'Get started').click()
    cy.location('pathname').should('eq', '/signup')
    cy.contains('h1', 'Create your account').should('be.visible')
  })
})

The test follows setup, action, assertion: visit the starting page, click the link, and verify both the destination and visible result. Cypress bundles Mocha’s describe and it and Chai’s assertion style; Cypress commands such as cy.visit(), cy.contains(), and .click() provide browser interaction. The first E2E test tutorial explains this workflow.

Prefer selectors tied to user-visible behavior, such as accessible roles and labels or stable text, when practical. For controls without reliable accessible names, add a stable test attribute such as data-cy="submit-order" and query that. Avoid selectors coupled to styling classes or deeply nested DOM structure; those tend to break when presentation changes.

5. Run tests locally

Use the interactive runner while authoring and debugging:

npx cypress open

Select the E2E testing type, choose a browser, and click the spec. The runner shows commands and the application as the test progresses. For a repeatable command-line run, use:

npx cypress run

cypress run runs to completion and is headless by default. You can choose a browser or spec explicitly:

npx cypress run --browser chrome
npx cypress run --spec 'cypress/e2e/sign-up.cy.js'

Use npx cypress run --help and the CLI reference for current flags. Add shared scripts to package.json so the team has consistent commands:

{
  "scripts": {
    "cy:open": "cypress open",
    "cy:run": "cypress run"
  }
}

Then run npm run cy:open or npm run cy:run. Keep any browser-specific or environment-specific flags in a documented team command or CI configuration.

6. Make tests reliable and useful

  • Assert outcomes. Check the visible state, URL, or persisted result that matters to the user, not just that a command completed.
  • Control test data. Use known fixtures or a supported test setup path so each run starts from a predictable state. Avoid depending on leftover data from a previous run.
  • Keep tests independent. Each spec should establish its own prerequisites instead of relying on another test to run first.
  • Use Cypress’s retry behavior. Cypress retries many queries and assertions while the app updates. Prefer a query followed by an assertion over fixed waits such as cy.wait(2000); fixed delays make tests slower and still fail when the app takes longer.
  • Wait for meaningful readiness. If a flow depends on a network response or a particular page state, synchronize with that condition and assert the resulting UI rather than guessing a delay.
  • Limit scope to critical journeys. External services, third-party scripts, and deployed environments can add instability. Use E2E coverage where the full journey matters and test other behavior at a narrower level when appropriate.

Cypress cautions against starting a web server from inside Cypress test scripts. Start it through your development or CI workflow and ensure it is available before a visit. Testing a deployed application can be useful, but account for disruption and failures from dependencies outside your control.

7. Run Cypress in CI

A CI job needs to install dependencies, start the application, wait until it responds, and then run Cypress. Do not rely on npm start & npx cypress run alone: the server may not have finished starting when the tests begin. Cypress’s CI guide recommends a readiness check rather than an arbitrary fixed sleep.

One common approach is to use a wait utility such as wait-on. Add it as a development dependency, then use a script that starts the server and waits for the local URL before running Cypress:

npm install --save-dev wait-on
{
  "scripts": {
    "start:test": "your-app-start-command",
    "cy:ci": "start-server-and-test start:test http://localhost:3000 cypress:run",
    "cypress:run": "cypress run"
  }
}

This script uses start-server-and-test, which must also be installed as a development dependency (npm install --save-dev start-server-and-test). Replace your-app-start-command with the project’s real server command and the URL with its readiness endpoint. Alternatively, configure your CI provider to start the server and use its supported wait mechanism. The key requirement is that the job confirms readiness before cypress run.

For example, a generic shell sequence can use a wait utility to check the URL before proceeding:

npm ci
npm run start:test &
npx wait-on http://localhost:3000
npx cypress run

Ensure the background server is cleaned up when the job finishes, using your CI runner’s process management or a server orchestration utility. Configure the required environment variables and test data in the job, and choose a browser that matches your support goals. Cypress documents setup examples for major CI providers in its continuous integration overview.

8. Troubleshooting common failures

Symptom Likely cause Fix
Cypress fails to install or its binary is missing Platform requirements or binary postinstall did not complete; npm script policy may affect newer npm versions. Check the current installation requirements, required Linux libraries, and the guide’s npm allowScripts instructions. Re-run the documented install or verification steps for your environment.
cy.visit() gets connection refused or times out The application is not running, the port or base URL is wrong, or CI launched Cypress before server readiness. Start the app separately, verify the URL in a browser or with a readiness check, and align baseUrl with the actual server address.
Element not found The selector or expected text does not match, the element is conditional, or the page has not reached the expected state. Inspect the app in the runner, verify the accessible name/text and route, and wait on a meaningful state or response. Avoid adding a guessed fixed sleep.
Test passes locally but fails in CI Different browser, viewport, environment variables, test data, or startup timing; possibly a dependency on shared state. Match required environment and browser settings, make setup deterministic, isolate tests, and wait for the server and application state explicitly.
Tests are flaky around network requests The test assumes timing or an external service response that varies. Control test data and dependencies where possible; synchronize with the relevant response and assert the UI outcome. Avoid depending on third-party availability for core app tests.
Wrong browser launches or browser is unsupported The selected browser is missing or outside Cypress’s current support range. Install/select a supported browser and check the current browser support details in the installation guide. Treat WebKit as experimental per the current documentation and recheck status before relying on it.

9. Performance, reliability, and cost

E2E coverage exercises more of the stack than component tests, so each test generally needs a running app and realistic setup. Keep the suite focused on high-value journeys, remove redundant steps, and avoid fixed waits to keep feedback useful. Parallel execution and browser choice depend on your CI provider, project setup, and Cypress configuration; consult the current CI and CLI documentation for supported options rather than assuming a particular speedup.

Reliability mostly comes from deterministic application state, a ready server, stable selectors, and assertions tied to real outcomes. A passing run only confirms the configured specs and environment; it does not establish that every browser or production integration behaves the same way. Run against the browsers your users rely on and keep browser support current.

Cypress is installed as project software; the research dossier identifies no physical product required for this workflow. Budget for the infrastructure and CI execution your project uses, and consult Cypress’s current plan information separately if evaluating hosted services. No unsupported performance benchmark or price estimate is needed to follow this tutorial.

Or skip the browser setup

If your goal is to capture a clean page image while documenting a flow, ScreenshotNeo is a website screenshot API and MCP server. It does not replace Cypress assertions or end-to-end tests, but it can produce screenshots without you configuring a browser capture stack. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners are accepted and removed before capture; known newsletter popups and chat widgets are removed too. Each cleanup step can be turned off.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Responses identify the page verdict and billing status in headers.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients.
  • The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

FAQ

Should I use Cypress for every UI behavior?

No. Use E2E tests for important whole-application journeys; use component tests or other narrower checks when they cover isolated behavior more directly.

Can Cypress run tests against a deployed site?

Yes, when that matches the goal, but external dependencies and deployment changes can make results less predictable. Cypress’s guidance treats local development as the main workflow.

Does a screenshot API verify that a journey works?

No. A screenshot captures page output; it does not replace Cypress actions, assertions, or application test setup.