---
title: "Verbose Test"
type: "test-smell"
slug: "verbose-test"
url: "http://localhost:3000/en/test-smells/verbose-test.md"
category: "Obscure Smells"
description: "A test that uses far more code than it needs to state its scenario, burying the one cause-and-effect it verifies under setup boilerplate, irrelevant data, and field-by-field assertions."
---
# Verbose Test

> A test that uses far more code than it needs to state its scenario, burying the one cause-and-effect it verifies under setup boilerplate, irrelevant data, and field-by-field assertions.

## Signs and Symptoms

You can't tell what a test proves at a glance, even though nothing tricky is going on — the intent is drowned in volume. Common tells:

* The test method is **long** (the Test Smells catalog uses a rough threshold of **\>30 lines**), or scrolls past one screen.
* A large `arrange` block builds objects field-by-field, much of it irrelevant to the behavior under test.
* Lots of **hard-coded literals** with no obvious link between inputs and the asserted outputs.
* The result is checked with a **pile of single-field assertions** instead of one meaningful comparison.
* Reading the assertions, you can't easily map which input caused which expected value.

```js
test('places an order for a returning customer', () => {
  const address = new Address();
  address.street = '123 Main St';
  address.city = 'Springfield';
  address.zip = '00000';                          // irrelevant to this test
  address.country = 'US';

  const customer = new Customer();
  customer.id = 42;
  customer.firstName = 'Ada';
  customer.lastName = 'Lovelace';
  customer.email = 'ada@example.com';
  customer.address = address;
  customer.createdAt = new Date('2020-01-01');     // noise

  const product = new Product();
  product.sku = 'SKU-1';
  product.name = 'Widget';
  product.price = 9.99;

  const cart = new Cart();
  cart.add(product, 2);

  const order = orderService.placeOrder(customer, cart);

  expect(order.status).toBe('CONFIRMED');          // the only lines that matter
  expect(order.lineItems.length).toBe(1);
  expect(order.lineItems[0].sku).toBe('SKU-1');
  expect(order.lineItems[0].quantity).toBe(2);
  expect(order.total).toBe(19.98);
  expect(order.customerId).toBe(42);
});

```

Roughly 25 lines of construction guard a two-line assertion about totals and status.

## Reasons for the Problem

**Why it happens**

* _Inline construction with required fields._ Building real objects through constructors/setters forces you to supply data the test doesn't care about, just to make the code compile and run. Meszaros notes the level of detail needed to make a test executable can make it "so verbose as to be difficult to understand."
* _Copy-paste growth._ A test starts small, then each new scenario is created by copying the previous one and tweaking a value, so noise accumulates.
* _No shared construction helpers._ Without Creation Methods, builders, or fixtures, every test re-states the full object graph.
* _Hard-coded data and field-by-field verification._ Writing literals everywhere and asserting on each property feels "thorough," but multiplies line count.

**Why it hurts**

* _Readability._ Verbose Test is a flavor of **Obscure Test** — the catalog even lists "Obscure Test" as its alias. The reader can't see the cause-and-effect between fixture and outcome, which is the whole point of an example-based test.
* _Maintainability._ A 30+ line test tends to carry "several responsibilities," so a small production change ripples into many lines across many tests. Bulk inline setup also breeds duplication.
* _False confidence._ Long, noisy tests hide bugs in plain sight: an incorrect literal or a misplaced assertion is easy to miss, and verbosity makes reviewers skim. The volume looks rigorous without proving more.
* _Reliability._ The more irrelevant state a test pins down, the more likely it breaks for reasons unrelated to its intent (over-specification), producing brittle, low-signal failures.

## Treatment

Push the noise out of the test body so only the variables that drive the behavior remain visible. Concrete moves (all from xUnit Test Patterns):

1. **Extract Creation Methods / use a builder.** Replace inline object construction with an evocatively named helper that supplies sensible defaults; pass in only the values the test actually cares about (Parameterized Creation Method). This kills both boilerplate and Irrelevant Information.
2. **Default the irrelevant data inside the helper**, signalling "these values don't affect the outcome."
3. **Replace field-by-field checks with an Expected Object** (`toEqual`/`toMatchObject`) or a **Custom Assertion / verification method** that names the concept being verified.
4. **Verify one behavior per test.** If a single test is long because it exercises several outcomes (an Eager Test), split it.
5. **Parameterize** near-identical verbose tests with `test.each` / `it.each` instead of copy-pasting.

```js
// before  — see Signs & Symptoms (≈30 lines of setup + 6 assertions)

// after
test('confirms the order and charges line-item total', () => {
  const customer = aCustomer();                          // Creation Method, defaults
  const cart = aCartWith(aProduct({ price: 9.99 }), 2);  // only the relevant input

  const order = orderService.placeOrder(customer, cart);

  expect(order).toMatchObject({                          // Expected Object, not per-field
    status: 'CONFIRMED',
    total: 19.98,
  });
});

```

The scenario now reads in seconds: a customer buys two of a $9.99 product → order is confirmed with a $19.98 total. The helpers (`aCustomer`, `aProduct`, `aCartWith`) are shared, tested utilities, so the noise lives in one place instead of every test.

Guardrail: cap test-function length and assertion count in your linter (see detectors) so tests can't silently grow back into Verbose Tests.

## Detected by

- **eslint** `max-lines-per-function` — Enforce a maximum number of lines per function — flags test bodies that exceed the configured limit, the core measure of a Verbose Test. (https://eslint.org/docs/latest/rules/max-lines-per-function)
- **eslint** `max-statements` — Enforce a maximum number of statements in a function block — caps how much a single test (an arrow/function callback) can do. (https://eslint.org/docs/latest/rules/max-statements)
- **sonar** `S138` — Functions should not have too many lines of code — applies to test functions; flags oversized, multi-responsibility test methods (RSPEC-138). (https://rules.sonarsource.com/javascript/RSPEC-138/)
- **eslint-jest** `jest/max-expects` — Enforce a maximum number of expect() calls per test (default 5) — catches the assertion-pile facet of verbose/eager tests. (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() calls per test (default 5) — Vitest equivalent of jest/max-expects. (https://github.com/vitest-dev/eslint-plugin-vitest/blob/main/docs/rules/max-expects.md)
