---
id: ui-coverage/guides/reduce-test-duplication
title: Reduce test duplication with UI Coverage
description: >-
  A step-by-step workflow for using Cypress UI Coverage to find the elements
  your suite tests far more than it needs to, then consolidate that repeated
  setup to cut CI time without losing coverage.
section: ui-coverage
source_path: docs/ui-coverage/guides/reduce-test-duplication.mdx
version: dd1b4f9adc7e2fae428f3afbd4b3686f68b7cdc9
updated_at: '2026-09-12T13:36:55.674Z'
---
# Reduce test duplication

Most test suites spend real time repeating work that no test is actually checking. Every test that clicks through the same login, onboarding, or cookie banner just to reach what it means to verify repeats that setup on every run, and those minutes add up across a suite and across CI.

UI Coverage turns each recorded run into a ranked list of the elements your tests exercise, with a count of how many times and how many separate tests touched each one. The controls at the top of that list are usually shared setup steps you can consolidate: collapse them into one programmatic step and you cut CI time and make the report easier to read, without losing any coverage.

This workflow takes you from a recorded run to a leaner, faster suite:

1.  [Record a run](#Step-1-Record-a-run-to-Cypress-Cloud) so Cypress Cloud can build a UI Coverage report.
2.  [Find your most-tested elements](#Step-2-Find-your-most-tested-elements) in Cypress Cloud or from your editor.
3.  [Tell intentional coverage from pass-through](#Step-3-Tell-intentional-coverage-from-pass-through) by drilling into an element.
4.  [Consolidate the repeated setup](#Step-4-Consolidate-the-repeated-setup) with a programmatic step instead of UI clicks.
5.  [Confirm the duplication dropped](#Step-5-Confirm-the-duplication-dropped) on your next run.
6.  [Keep duplication from creeping back](#Step-6-Keep-duplication-from-creeping-back).

## Step 1: Record a run to Cypress Cloud

UI Coverage needs a recorded run to analyze. Any run recorded to Cypress Cloud with [Test Replay](/llm/markdown/cloud/features/test-replay.md) produces a report automatically, with no configuration or code changes. Use a run that reflects your suite as it normally executes, such as a full regression run, so the interaction counts are representative.

## Step 2: Find your most-tested elements

However you prefer to work, the goal of this step is the same: a list of the elements your tests exercise, ranked so the most-tested rise to the top.

*   **In Cypress Cloud**, open the run's **UI Coverage** tab and go to the **Tested elements** section. It lists every interactive element your tests exercised, one row per element or [group](/llm/markdown/ui-coverage/core-concepts/element-grouping.md), sorted by **Interactions** with the highest first.
*   **From your editor**, ask an AI agent connected to [Cypress Cloud MCP](/llm/markdown/cloud/integrations/cloud-mcp.md) to pull the tested elements for the run and rank the most-exercised controls. It reads the same data, so you get the same ranked list without opening a browser tab. See [Work with AI agents](/llm/markdown/ui-coverage/work-with-ai-agents.md) for prompts.

Each element carries a few counts, and two of them tell you what you need:

*   **Interactions**: how many times a recognized command targeted the element. A high number can come from one test hitting it in a loop, which usually isn't duplication.
*   **Tests**: how many _separate_ tests interacted with it. A high number here means many separate tests all touch the same control, which is the signature of shared setup repeated across your suite.

The **Tests** count is the clearer duplication signal, so look for controls that a large share of your suite touches. A "Submit" button covered by a handful of form tests is expected. A "Continue" button on a welcome screen, or a "Log in" button, that shows up in hundreds of tests is a strong signal those tests are passing _through_ a flow to reach something else.

### Find duplication candidates with MCP

Have an agent rank your most-tested elements and flag the ones that look like shared setup.

Using Cypress Cloud, pull the UI Coverage tested elements for the latest run on this branch. Rank them by how many separate tests interact with each element, then by total interactions, and for the highest-ranked ones tell me which views they appear in. Flag the ones that look like shared setup a test passes through rather than verifies — logging in, onboarding, cookie or consent banners — and give me a shortlist of consolidation candidates with the element name, its test and interaction counts, and where it appears.

## Step 3: Tell intentional coverage from pass-through

A high count isn't automatically a problem. Before changing anything, confirm the interactions are incidental rather than the point of the tests. Expand the element to break its counts down by view, with a **Test Replay** link for each occurrence:

Open [Test Replay](/llm/markdown/cloud/features/test-replay.md) on a few of the tests. Ask:

*   **Is any assertion about this element?** If tests interact with it but never assert on its behavior, they're passing through, not testing it.
*   **Do the tests share the same purpose?** Dozens of unrelated tests clicking the same "Continue" button are all paying for setup that belongs somewhere cheaper.
*   **Does the interaction happen before the test's real work begins?** Setup that runs at the start of nearly every test is the prime candidate to consolidate.

In the example above, the "Continue" button on the welcome screen is interacted with by most of the suite, but Test Replay shows almost none of those tests care about the welcome screen. They're just getting past it to reach the projects page. That's the duplication to remove.

## Step 4: Consolidate the repeated setup

Once you've confirmed a control is pass-through setup, replace the repeated UI interaction with a single programmatic step. Which technique fits depends on what the setup does:

| When the pass-through is… | Reach for |
| --- | --- |
| Logging in through a form | [`cy.session()`](/llm/markdown/api/commands/session.md) to cache and restore the login |
| A modal, banner, or onboarding step gated by a flag | Set the cookie or storage value directly with [`cy.setCookie()`](/llm/markdown/api/commands/setcookie.md) |
| Building up data your test needs to exist first | Seed it through your API with [`cy.request()`](/llm/markdown/api/commands/request.md), or run backend setup with [`cy.task()`](/llm/markdown/api/commands/task.md) |
| Waiting on a network response to reach a state | Stub it with [`cy.intercept()`](/llm/markdown/api/commands/intercept.md) |

### Cache authentication with `cy.session()`

Logging in through the UI is the most common pass-through in any suite. Wrap it in [`cy.session()`](/llm/markdown/api/commands/session.md) so the login runs once and every later test restores the cached session instead of driving the form again:

*   JavaScript
*   TypeScript

cypress/support/commands.js

```
Cypress.Commands.add('login', (username = 'user@example.com') => {
  cy.session(username, () => {
    cy.visit('/login')
    cy.get('[data-cy="email"]').type(username)
    cy.get('[data-cy="password"]').type('super-secret')
    cy.get('[data-cy="submit"]').click()
    cy.url().should('include', '/projects')
  })
})
```

cypress/support/commands.ts

```
declare global {
  namespace Cypress {
    interface Chainable {
      login(username?: string): Chainable<void>
    }
  }
}

Cypress.Commands.add('login', (username = 'user@example.com') => {
  cy.session(username, () => {
    cy.visit('/login')
    cy.get('[data-cy="email"]').type(username)
    cy.get('[data-cy="password"]').type('super-secret')
    cy.get('[data-cy="submit"]').click()
    cy.url().should('include', '/projects')
  })
})
```

Keep one dedicated test that drives the login form through the UI so the flow itself stays covered, then call `cy.login()` everywhere else. UI Coverage still marks the form as tested from that one test, while the interaction count on it drops from hundreds to one. When your login lives on a separate identity provider, pair `cy.session()` with [`cy.origin()`](/llm/markdown/api/commands/origin.md) to drive the form across the origin boundary.

### Set state directly instead of clicking through it

When the pass-through is a modal, banner, or onboarding step gated by a cookie or stored flag, set that state before the page loads rather than dismissing it in the UI each time:

*   JavaScript
*   TypeScript

cypress/support/commands.js

```
// Skip the first-run welcome screen by pre-setting the flag it checks
Cypress.Commands.add('skipWelcome', () => {
  cy.setCookie('welcome-dismissed', 'true')
})
```

cypress/support/commands.ts

```
declare global {
  namespace Cypress {
    interface Chainable {
      skipWelcome(): Chainable<void>
    }
  }
}

// Skip the first-run welcome screen by pre-setting the flag it checks
Cypress.Commands.add('skipWelcome', () => {
  cy.setCookie('welcome-dismissed', 'true')
})
```

Moving the setup into a `beforeEach` hook is what multiplies the interaction count in the first place, so it's also where the fix belongs: the projects specs skip the welcome screen once per test in setup, while one dedicated test still covers the welcome flow on purpose.

cypress/e2e/projects.cy.js

```
describe('Projects', () => {
  it('shows the welcome screen to first-time users', () => {
    cy.visit('/')
    cy.contains('Welcome')
    cy.get('[data-cy="continue"]').click()
    cy.contains('Projects')
  })

  describe('for returning users', () => {
    beforeEach(() => {
      cy.skipWelcome()
      cy.visit('/')
    })

    it('lists the current user projects', () => {
      cy.contains('Projects')
    })

    // Every other test starts past the welcome screen without clicking Continue
  })
})
```

The same approach works for seeding data with [`cy.request()`](/llm/markdown/api/commands/request.md) against your API, or stubbing a response with [`cy.intercept()`](/llm/markdown/api/commands/intercept.md), instead of building that state by hand through the UI in every test.

An agent can do this consolidation for you, combining the Cloud data about which tests touch the control with your local specs and support files:

### Consolidate a pass-through flow with MCP

Have an agent replace repeated UI setup with a single programmatic step across your specs.

Take the login flow that most of my suite passes through. Using Cypress Cloud UI Coverage for the latest run on this branch, find the specs that exercise it, then consolidate that setup in my project: add a cy.session() wrapper in my support files (or set the cookie/flag directly, or seed with cy.request()/cy.task() where that fits better), following the patterns already in my codebase. Keep one test that still drives the flow through the UI so its coverage is preserved, and update the other specs to call the new command instead of repeating the UI steps.

## Step 5: Confirm the duplication dropped

Record a new run after consolidating, then reopen the **Tested elements** section. The control you consolidated should sit lower in the list: its **Interactions** and **Tests** counts fall to reflect the one test that still exercises it on purpose, and its coverage is unchanged because that test still marks it tested.

To see the change directly, use [Branch Review to compare the two runs](/llm/markdown/ui-coverage/guides/compare-reports.md): the before-and-after makes it easy to confirm the element's interactions dropped while your overall coverage score held steady.

The time you saved shows up outside UI Coverage, in how long the suite takes to run. Watch the run duration on the [runs list](/llm/markdown/cloud/features/recorded-runs.md#Latest-Runs) or in [Test Replay](/llm/markdown/cloud/features/test-replay.md) fall as the redundant UI steps come out, so you can put a number on the payoff.

## Step 6: Keep duplication from creeping back

Consolidation sticks only if new tests reuse the shared setup instead of re-introducing the UI steps. A few habits keep the list clean:

*   **Reach for shared commands first.** Make `cy.login()`, `cy.skipWelcome()`, and similar setup commands the default way to get a test to its starting point, so no one hand-rolls the flow again.
*   **Recheck the top of the list periodically.** Revisit the **Tested elements** section after big changes, or on a [regular cadence](/llm/markdown/ui-coverage/guides/monitor-changes.md), and treat a control climbing back toward the top as a prompt to consolidate again.
*   **Write down the boundary.** Note which flows are covered once on purpose (the login form, the welcome screen) and which should always be reached programmatically, so the intent survives beyond the person who set it up.

Time you get back from a leaner suite is best spent on the flows you _haven't_ covered yet. See [Identify coverage gaps](/llm/markdown/ui-coverage/guides/identify-coverage-gaps.md) to find them.

## See also

*   [Identify coverage gaps](/llm/markdown/ui-coverage/guides/identify-coverage-gaps.md): find the untested elements and pages to spend your reclaimed time on.
*   [Reduce noise](/llm/markdown/ui-coverage/guides/reduce-noise.md): sharpen a report that over-counts elements, the report-accuracy counterpart to this test-effort guide.
*   [Compare reports with Branch Review](/llm/markdown/ui-coverage/guides/compare-reports.md): confirm interactions dropped and coverage held between your before-and-after runs.
*   [Monitor changes](/llm/markdown/ui-coverage/guides/monitor-changes.md): track the report over time so duplication doesn't creep back.
*   [Cypress Cloud MCP](/llm/markdown/cloud/integrations/cloud-mcp.md): query a run's UI Coverage from your editor, as in the prompts above.
*   [Work with AI agents](/llm/markdown/ui-coverage/work-with-ai-agents.md): more prompts and patterns for pointing an agent at UI Coverage data.
*   [Test Replay](/llm/markdown/cloud/features/test-replay.md): the recorded run data UI Coverage is built from, and where you inspect each interaction.
*   [`cy.session()`](/llm/markdown/api/commands/session.md): cache and restore login so authentication runs once instead of in every test.
*   [UI Coverage FAQ](/llm/markdown/ui-coverage/faq.md#Reducing-test-duplication): common questions about over-tested elements and consolidating setup.
