Parameterized tests should not contain duplicate test cases. Each row in a parameterized test dataset should describe a distinct scenario.

Why is this an issue?

Parameterized tests execute their test body once for every row in the dataset. Repeating a row therefore runs the same parameterized test case more than once. This increases test execution time and makes the intended test matrix harder to understand.

This rule reports duplicate rows in the Jest, Vitest, and Bun parameterized test and suite declarations that use .each, including test.each, it.each, describe.each, and Vitest’s suite.each. Rows are compared as complete abstract syntax tree expressions, so the rule covers identifiers and function calls in addition to primitive literals.

The comparison is structural and does not evaluate expressions. For example, two calls to createUser('Alice') are considered duplicates even if the function returns a new object each time. For each structurally repeated case, the rule raises one issue on the first duplicate. Secondary locations identify the original case and any additional duplicates. A quick fix is available to remove the redundant cases.

Exceptions

Repeated rows can be intentional when the test body mutates a shared object or relies on mutable global state. Review the test before applying the quick fix because removing a repeated execution is not always safe.

Code examples

Noncompliant code example

import { test } from '@jest/globals';

test.each([
  1,
  2,
  1, // Noncompliant: this duplicates the first case
])('calculates square of %i', (num) => {
  expect(square(num)).toBe(num * num);
});

Compliant solution

import { test } from '@jest/globals';

test.each([
  1,
  2,
  3,
])('calculates square of %i', (num) => {
  expect(square(num)).toBe(num * num);
});

Noncompliant code example

import { test } from 'vitest';

const alice = { name: 'Alice', role: 'admin' };
const bob = { name: 'Bob', role: 'user' };

test.each([
  [alice, 'admin'],
  [bob, 'user'],
  [alice, 'admin'], // Noncompliant: this duplicates the first case
])('accepts %s with the %s role', (user, role) => {
  expect(user.role).toBe(role);
});

Compliant solution

import { test } from 'vitest';

const alice = { name: 'Alice', role: 'admin' };
const bob = { name: 'Bob', role: 'user' };
const carol = { name: 'Carol', role: 'editor' };

test.each([
  [alice, 'admin'],
  [bob, 'user'],
  [carol, 'editor'],
])('accepts %s with the %s role', (user, role) => {
  expect(user.role).toBe(role);
});

Noncompliant code example

import { test } from 'bun:test';

const makeGreeting = name => `Hello, ${name}!`;

test.each([
  [makeGreeting('Alice'), 'Hello, Alice!'],
  [makeGreeting('Bob'), 'Hello, Bob!'],
  [makeGreeting('Alice'), 'Hello, Alice!'], // Noncompliant: this duplicates the first case
])('creates the expected greeting', (greeting, expected) => {
  expect(greeting).toBe(expected);
});

Compliant solution

import { test } from 'bun:test';

const makeGreeting = name => `Hello, ${name}!`;

test.each([
  [makeGreeting('Alice'), 'Hello, Alice!'],
  [makeGreeting('Bob'), 'Hello, Bob!'],
  [makeGreeting('Carol'), 'Hello, Carol!'],
])('creates the expected greeting', (greeting, expected) => {
  expect(greeting).toBe(expected);
});

Resources

Documentation