Skip to content
VitestDedup

v0.5.0 · MIT · static analysis

Find duplicated tests.Reduce redundancy.Keep your test suite maintainable.

VitestDedup reads your Vitest test files — never importing or executing them — and reports duplicate and near-duplicate tests: identical bodies hiding under different titles, structural copies that differ only in literals, whole suites repeated across files.

Every command shown on this site is real. Sample output is marked as illustrative.

vitest-dedup
$ npx vitest-dedup .
vitest-dedup 0.5.0

duplicate-test (1 error)
  error  same normalized body under an equivalent context
    --> tests/auth/login.spec.ts:14:1 redirects when signed out
    related: tests/auth/session.spec.ts:22:1 same test
    action: review removal or repair of intended coverage

near-duplicate-test (1 warning)
  warning  same behavior under an equivalent context
    --> tests/api/users.spec.ts:8:1 lists admin users
    related: tests/api/admin-users.spec.ts:31:1 similar
    action: review before changing tests

318 tests in 42 files analyzed
errors: 1, warnings: 2, off: 0 · threshold: 0%
Analysis policy: FAIL
Detection time: 412.7 ms

Real command · output abridged and illustrative

---

Test suites grow. Duplication compounds.

Copy-pasting a test and tweaking a value is the fastest way to add coverage today — and the slowest way to change anything tomorrow. As a suite grows, duplicated tests quietly pile up:

  • A refactor touches the same logic in three places, or none of them get updated.
  • Redundant cases keep executing on every run, inflating CI time.
  • Nobody can say which test is authoritative, so failures get skimmed instead of read.
  • Reviewing new tests gets harder because near-copies look intentional.

A test's identity is its body

The analyzer's core judgment, in two sentences:

Two tests that do the same thing under different titles are duplicates. Two textually identical bodies that run under different hooks, mocks, or fixtures are not.

That distinction is why VitestDedup is an analysis tool rather than an auto-fixer. It names what each finding claims — a verdict with a remedy— and the decision to consolidate stays with you.

Features

Everything the analyzer actually does

No invented capabilities — each of these is shipped behavior ofv0.5.0, documented in thereference.

Reports

What a run tells you

Every finding carries a rule, a severity, a verdict, stable fingerprints, and full participant locations — the fields the json andsarif reporters write for machines.

tests analyzed
318
files analyzed
42
findings
6
detection time
412.7 ms
redundanttests/auth/login.spec.ts ↔ session.spec.tserror

rule duplicate-test · fingerprintduplicate-test:tests/auth/login.spec.ts:redirects-unauthenticated-users

two tests have the same normalized body under an equivalent context.

  • tests/auth/login.spec.ts:14:1“redirects unauthenticated users to /login”
  • tests/auth/session.spec.ts:22:1“redirects unauthenticated users to /login”

Action: review removal of one copy or repair of intended coverage.

consolidatabletests/cart/totals.spec.ts ↔ totals.spec.tswarning

rule parameterization-candidate · fingerprintparameterization-candidate:tests/cart/totals.spec.ts:applies-discount-code

same structure, differing only in literal values.

  • tests/cart/totals.spec.ts:31:1“applies discount code SAVE10”
  • tests/cart/totals.spec.ts:39:1“applies discount code SAVE20”

Action: preserve every case and assertion in one test.each table.

similar — reviewtests/api/users.spec.ts ↔ admin-users.spec.tswarning

rule near-duplicate-test · fingerprintnear-duplicate-test:tests/api/users.spec.ts:lists-users-for-admin

same behavior under an equivalent context, but normalized bodies differ.

  • tests/api/users.spec.ts:8:1“lists users for an admin requester”
  • tests/api/admin-users.spec.ts:31:1“lists users for an admin requester”

Action: review before changing tests; the usual outcome is one test.each table.

name collisiontests/schemas/order.spec.ts ↔ order.spec.tswarning

rule duplicate-test-name · fingerprintduplicate-test-name:tests/schemas/order.spec.ts:validates-payload

two tests in one file share a full title.

  • tests/schemas/order.spec.ts:12:3“validates the payload”
  • tests/schemas/order.spec.ts:57:3“validates the payload”inert

Action: rename one of the tests.

redundanttests/unit/format.spec.ts ↔ invoice.spec.tswarning

rule cross-file-duplicate · fingerprintcross-file-duplicate:tests/unit/format.spec.ts:formats-currency

duplicate-test criteria without the single-file restriction.

  • tests/unit/format.spec.ts:6:1“formats currency amounts”
  • tests/integration/invoice.spec.ts:88:1“formats currency amounts”

Action: review removal; duplication across unit and integration layers is sometimes deliberate.

redundanttests/db/repository.spec.tswarning

rule redundant-hook · fingerprintredundant-hook:tests/db/repository.spec.ts:beforeEach-seed

a nested lifecycle hook repeats work an ancestor hook already performs.

  • tests/db/repository.spec.ts:18:3“beforeEach seeds the database”

Action: remove the nested hook.

Illustrative findings shaped after the documented JSON report fields (rule, verdict, participants, fingerprint). Not output generated by the CLI. Structured to consume real --reporters json output later without changing these components.

Workflow

From first run to a gate you can trust

  1. 1

    Install

    A prebuilt binary for your platform arrives through optionalDependencies — no postinstall script, nothing compiled or executed at install time.

    npm install --save-dev vitest-dedup
  2. 2

    Analyze

    Discovery reads your Vitest configuration statically, then every test file is parsed and compared. Nothing is imported or run.

    npx vitest-dedup .
  3. 3

    Triage

    Each finding carries a verdict and an action. Deliberate parallels get a vitest-dedup-disable-next-line comment right in the test.

    vitest-dedup . --reporters html
  4. 4

    Gate

    Record existing debt once, then fail only for what new work adds. In CI, the GitHub Action verifies the binary before running it.

    vitest-dedup . --baseline dedup-baseline.json