Hero Background

Next-Gen App & Browser Testing Cloud

Trusted by 2 Mn+ QAs & Devs to accelerate their release cycles

Next-Gen App & Browser Testing Cloud

How to manage test dependencies in Cypress?

The phrase "test dependencies" in Cypress means two different things, and it helps to separate them right away. The first is the set of npm packages your project needs to run, which are declared in package.json and installed with npm install. The second is the dependencies between tests, that is, shared data, login state, or execution order that makes one test rely on another. Cypress's core philosophy is that tests should run independently, so most between-test dependency problems are solved by removing hidden coupling rather than managing it. This guide covers both meanings with accurate, current (Cypress 13/14) guidance.

Managing npm / package dependencies in a Cypress project

Package dependencies are not a Cypress concept at all, they are standard Node.js dependencies. They live in package.json and are resolved by your package manager. Cypress itself is normally a development dependency, and so are reporters and plugins.

  • package.json is the source of truth: runtime packages go under dependencies and tooling such as Cypress, reporters, and plugins goes under devDependencies.
  • npm install resolves everything: running npm install (or npm ci for a clean, lockfile-exact install in CI) downloads every package declared in package.json.
  • Cypress is a devDependency: add it with npm install --save-dev cypress so it is pinned in package.json and installs the same way on every machine.
  • Plugins are packages, not config entries: tools like cypress-axe, @cypress/grep, or mochawesome are devDependencies you install and then register, never something you "declare" inside a Cypress config file.
{
  "name": "my-cypress-project",
  "scripts": {
    "cy:open": "cypress open",
    "cy:run": "cypress run"
  },
  "devDependencies": {
    "cypress": "^14.0.0",
    "cypress-axe": "^1.5.0",
    "@cypress/grep": "^4.1.0"
  }
}
# Install everything declared in package.json
npm install
# Or, for a clean lockfile-exact install in CI
npm ci
# Add a new dev dependency
npm install --save-dev cypress-axe

Project configuration with cypress.config.js (not cypress.json)

A common (and outdated) belief is that you manage dependencies in a cypress.json file. That file was deprecated in Cypress 10 and removed. Configuration now lives in cypress.config.js or cypress.config.ts through the defineConfig() helper. Crucially, this file holds configuration, such as the base URL, viewport, and Node event handlers, and it never declared npm packages even before the change.

  • defineConfig() wraps your settings: it gives you type hints and validates the shape of the config object.
  • e2e and component blocks: end-to-end and component testing each get their own configuration section.
  • setupNodeEvents is the plugin hook: this is where you register cy.task handlers and wire up plugins, replacing the old plugins/index.js file.
const { defineConfig } = require("cypress");

module.exports = defineConfig({
  e2e: {
    baseUrl: "http://localhost:3000",
    setupNodeEvents(on, config) {
      // Register Node-side tasks and plugins here
      on("task", {
        "db:seed": () => {
          // ...seed your test database, then return a value or null
          return null;
        },
      });
      return config;
    },
  },
});

Managing dependencies between tests (test isolation)

The second meaning of "test dependencies" is the relationship between tests, and here the goal is the opposite of npm packages: you want to eliminate dependencies, not collect them. A healthy Cypress suite follows a few rules.

  • Every test must pass alone: change an it to it.only and the test should still pass. If it only passes when earlier tests ran first, it is hiding a dependency.
  • Do not share state through variables: data stored in module-level or describe-level JavaScript variables to pass results from one test to the next is a classic source of flakiness.
  • testIsolation resets state for you: in Cypress 12+ the testIsolation option defaults to true, clearing cookies, localStorage, and sessionStorage before each test so leftover state cannot leak between them.

Set up shared state with before / beforeEach hooks

The right place to put the setup a group of tests depends on is a before or beforeEach hook. Cypress recommends putting your state in a known condition before each test, rather than cleaning up after it, because an after hook is not guaranteed to run if a test crashes. Resetting up front guarantees a clean starting point every time.

describe("Dashboard", () => {
  beforeEach(() => {
    // Runs before EVERY test, guaranteeing a known starting state
    cy.visit("/dashboard");
  });

  it("shows the welcome banner", () => {
    cy.contains("Welcome").should("be.visible");
  });
});

Seed data via the API with cy.request

When a test needs a user, an order, or any record to exist first, do not drive a previous UI test to create it. Create that data directly through your application's API with cy.request. This is faster than clicking through the UI and, more importantly, it decouples the test from any other test.

beforeEach(() => {
  // Create the data this test needs directly via the API,
  // instead of depending on a previous UI test to create it.
  cy.request("POST", "/api/users", {
    name: "Test User",
    email: "[email protected]",
  });
});

Reset and seed back-end state with cy.task and cy.exec

For setup that has to run in Node rather than the browser, such as resetting a database, Cypress gives you two commands. cy.task() runs Node code registered in setupNodeEvents, and cy.exec() runs a shell command or script. Both are for setup and teardown of system state. To be explicit: neither installs npm packages, that is what npm install is for.

// Runs Node code from setupNodeEvents in cypress.config.js
beforeEach(() => {
  cy.task("db:seed");
});

// cy.exec runs a shell command/script for system-level setup
beforeEach(() => {
  cy.exec("npm run db:reset");
});

Reuse setup logic with custom commands and fixtures

Repeated setup, like logging in, belongs in a reusable custom command rather than being copied into every spec. Static test data, a data dependency that does not change, belongs in a fixture read with cy.fixture() from the cypress/fixtures folder. For structuring larger amounts of shared, reusable code, see What Are Page Objects in Cypress?.

// cypress/support/commands.js
Cypress.Commands.add("login", (email, password) => {
  cy.request("POST", "/api/login", { email, password });
});

// In a test, load static data from cypress/fixtures/user.json
it("logs in", () => {
  cy.fixture("user.json").then((user) => {
    cy.login(user.email, user.password);
  });
});

Cache login and auth across tests with cy.session

The most common between-test dependency is "every test needs to be logged in." Instead of re-logging in through the UI each time, wrap the login in cy.session(). Cypress caches the cookies, localStorage, and sessionStorage under an id and restores them on later calls, so the login runs once. Add cacheAcrossSpecs: true to reuse it across spec files, and an optional validate block to confirm the cached session is still good.

beforeEach(() => {
  cy.session(
    "standard-user",
    () => {
      // Setup: runs once, then the result is cached and restored
      cy.login("[email protected]", "secret");
    },
    {
      cacheAcrossSpecs: true,
      validate() {
        cy.request("/api/me").its("status").should("eq", 200);
      },
    }
  );
});

Avoid test-order coupling (anti-patterns)

Most "dependency" bugs trace back to tests that secretly rely on each other. Watch for these patterns.

  • No chained tests: never write Test B so it only passes if Test A ran first and left something behind. Seed what B needs in its own hook.
  • No state-passing variables: do not stash IDs or tokens in describe-level variables to hand them to a later test. Re-create or re-fetch them per test.
  • No reliance on order: do not depend on alphabetical, file, or run order. Cypress can run specs in parallel and in different orders across machines.
  • Skip-on-prerequisite is a last resort: plugins such as cypress-prerequisite can skip dependent tests when a setup step fails, but the better default is a self-sufficient test that needs no prerequisite at all.

Run Cypress across browsers on the TestMu AI cloud grid

Once your dependencies are clean, environment consistency is the next thing that keeps a suite reliable. Running the same Cypress suite across many browser and version combinations on the TestMu AI Getting Started with Cypress Testing gives every run a reproducible, pre-provisioned environment, which removes a whole class of environment-driven "it works on my machine" failures. If your project relies on private npm packages, the Private Dependencies Cypress docs show how to supply them to the grid too, so package and configuration setup stays consistent everywhere your tests run.

Frequently Asked Questions

Is cypress.json still used to manage dependencies?

No. cypress.json was deprecated in Cypress 10 and removed; project configuration now lives in cypress.config.js or cypress.config.ts via defineConfig(). And no config file ever declared npm packages in the first place, those belong in package.json under dependencies or devDependencies.

Can cy.task or cy.exec install npm dependencies?

No. cy.task() runs Node code registered in setupNodeEvents and cy.exec() runs a shell command. They are used for setup and teardown, such as seeding or resetting a database, not for installing packages. Install packages with npm install (or npm ci) from your project root.

How do I make one Cypress test depend on another test's data?

You should not. Cypress is built around independent tests, so create the data each test needs in a before or beforeEach hook, usually by seeding through the API with cy.request or through the back end with cy.task. That way every test passes on its own and is immune to execution order.

How do I avoid logging in before every Cypress test?

Wrap the login in cy.session(). Cypress caches the resulting cookies, localStorage, and sessionStorage under an id and restores them on later calls, so the login only runs once. Add cacheAcrossSpecs: true to reuse the cached session across spec files.

Where do Cypress fixtures and static test data go?

Static test data lives in the cypress/fixtures folder as JSON, CSV, or similar files, and you read it inside a test with cy.fixture('file.json'). Fixtures are the right place for a data dependency that is fixed and reusable across tests.

How do I install a Cypress plugin?

Install it as a devDependency with npm install --save-dev <plugin>, then wire it up in cypress.config.js (for example inside setupNodeEvents) or in a support file. A plugin is a package recorded in package.json, not an entry in a Cypress config file.

Related Questions

Test Your Website on 3000+ Browsers

Get 100 minutes of automation test minutes FREE!!

Test Now...

KaneAI - Testing Assistant

World’s first AI-Native E2E testing agent.

...

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests