---
title: "Eager Test"
type: "test-smell"
slug: "eager-test"
url: "http://localhost:3000/en/test-smells/eager-test.md"
category: "Assertion Smells"
description: "An Eager Test verifies several distinct methods or behaviors of the unit under test in a single test method, instead of focusing on one behavior."
---
# Eager Test

> An Eager Test verifies several distinct methods or behaviors of the unit under test in a single test method, instead of focusing on one behavior.

## Signs and Symptoms

A single test method exercises many unrelated production methods and asserts on each, walking the object through a whole sequence of operations rather than checking one outcome.

Tell-tale signs:

* A vague, catch-all test name (`testUserService`, `it('works')`) or one stitched together with "and" (`creates_and_renames_and_deletes`).
* Multiple **act → assert** cycles in one body: you call a method, assert, call another, assert again.
* Comments used as section headers inside the test (`// now test delete`) to separate the things being checked.
* Assertions touch several different methods/fields of the SUT that don't share a single logical outcome.

```js
// SMELL: one test verifies create, rename, delete, and list
test('user service', () => {
  const svc = new UserService();

  const user = svc.create({ name: 'Ada' });   // behavior 1
  expect(user.id).toBeDefined();

  svc.rename(user.id, 'Grace');                // behavior 2
  expect(svc.get(user.id).name).toBe('Grace');

  svc.delete(user.id);                         // behavior 3
  expect(svc.get(user.id)).toBeUndefined();

  expect(svc.list()).toHaveLength(0);          // behavior 4
});

```

Note the distinction: an Eager Test is about testing **multiple behaviors**, not merely having multiple `expect`s. Several assertions on one logical result (e.g. checking several fields of the _same_ returned object) is fine and is not this smell.

## Reasons for the Problem

**Why it happens**

* **Setup reuse / laziness.** Arranging the fixture is tedious or expensive, so it's tempting to keep poking at the already-constructed object and assert "while we're here."
* **Workflow thinking.** The author tests a user journey ("create, then edit, then delete") as one script instead of isolating each unit behavior.
* **TDD drift.** A test that started focused accumulates extra assertions as new functionality is bolted on rather than given its own test.

**Why it hurts**

* **False confidence (the big one).** Most assertion libraries abort the test at the _first_ failing assertion. If `create` breaks, the `rename`, `delete`, and `list` checks never run — so a green-to-red flip hides how many behaviors are actually broken, and a passing test was never really exercising the later steps once an early one regressed.
* **Poor diagnostics.** A failure tells you "user service test failed," not _which_ behavior. You have to read the whole method to locate the broken step.
* **Obscured intent / readability.** The test no longer documents a single fact about the system; the reader must mentally segment it into the behaviors it bundles.
* **Fragility & coupling.** Later asserts depend on earlier mutations, so an unrelated change near the top cascades and makes the test brittle and hard to refactor.
* **Maintainability.** It's harder to delete, move, or rename a behavior's coverage when it's entangled with three others in one method.

## Treatment

Split the eager test into several focused tests — **one behavior per test** — and lift the shared arrange step into setup so the split doesn't duplicate boilerplate.

Steps:

1. **List the behaviors** the test bundles (here: create, rename, delete, list-after-delete).
2. **Extract one test per behavior**, each with a descriptive, intention-revealing name.
3. **Move shared setup** into a `beforeEach` or a factory/Creation Method so each test still has a clean SUT without copy-pasted arrange code.
4. **Keep a single Act** per test and assert only on that behavior's outcome (multiple `expect`s on the _same_ result are fine).
5. If you genuinely need to validate an end-to-end **workflow**, keep that as one explicitly-named scenario/integration test — but still cover each unit behavior in its own test rather than relying on the workflow for coverage.
6. **Guard against regression** by enabling a max-assertions rule (see detectors) as a cheap proxy.

```js
// AFTER: focused tests, shared setup
let svc;
beforeEach(() => { svc = new UserService(); });

test('create() assigns an id', () => {
  expect(svc.create({ name: 'Ada' }).id).toBeDefined();
});

test('rename() updates the stored name', () => {
  const { id } = svc.create({ name: 'Ada' });
  svc.rename(id, 'Grace');
  expect(svc.get(id).name).toBe('Grace');
});

test('delete() removes the user', () => {
  const { id } = svc.create({ name: 'Ada' });
  svc.delete(id);
  expect(svc.get(id)).toBeUndefined();
});

```

Now any single behavior can fail independently, the failing test name pinpoints the break, and each test reads as one documented fact about the SUT.

## Detected by

- **eslint-jest** `jest/max-expects` — Enforce a maximum number of expect() calls per test (https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/max-expects.md)
- **eslint-vitest** `vitest/max-expects` — Enforce a maximum number of expect per test (https://github.com/vitest-dev/eslint-plugin-vitest/blob/main/docs/rules/max-expects.md)
