How DAMP Applies Over DRY in Test Code: A Complete Guide

DAMP (Descriptive And Meaningful Phrases) prioritizes readable, self-contained test stories over the code reuse encouraged by DRY (Don't Repeat Yourself), making tests understandable without cross-referencing shared helper modules.

In the addyosmani/agent-skills repository, the test-driven development skill establishes a critical distinction between production and test code principles. While production code benefits from DRY to minimize duplication, test code demands the DAMP approach to serve as executable documentation. This ensures that every test reads like a specification, conveying intent explicitly rather than hiding logic behind shared abstractions.

Understanding DAMP vs DRY

According to skills/test-driven-development/SKILL.md at line 197, the canonical guidance states: "In production code, DRY (Don't Repeat Yourself) is usually right. In tests, DAMP (Descriptive And Meaningful Phrases) is better."

The Problem with DRY in Test Suites

When test suites over-optimize for DRY, they force readers to navigate between files to understand a single test scenario. Shared setup logic in external helper files obscures the actual test conditions, making debugging and maintenance more difficult. The README.md (line 149) in the addyosmani/agent-skills repository lists this concept as a featured pattern, emphasizing that production optimization techniques often hurt test readability.

Three Core Principles of DAMP

Full Story Per Test

Each test should describe what is being verified, not how common setup is performed. The test body must contain all context necessary to understand the scenario without importing setup logic from elsewhere.

Explicit Data

Test data should be inlined exactly where it belongs. Hardcoded values in the test body make the scenario obvious at a glance, rather than abstracting values behind factory functions that require mental mapping.

Minimal Indirection

Helper functions should remain tiny and localized. When helpers become reusable across many tests, they become candidates for refactoring only after the test suite stabilizes, not during initial writing.

Code Examples: DRY vs DAMP in Practice

The following examples from the addyosmani/agent-skills repository demonstrate the readability difference between the two approaches.

DRY-Styled Test (Harder to Read)

In this pattern, shared logic lives in a separate helpers file:

// helpers.js
export const createUser = (overrides = {}) => ({
  name: 'John Doe',
  email: '[email protected]',
  ...overrides,
});

// user.test.js
import { createUser } from './helpers';

test('should reject signup when email is missing', () => {
  const user = createUser({ email: '' });
  expect(() => signup(user)).toThrow('Email required');
});

test('should reject signup when name is missing', () => {
  const user = createUser({ name: '' });
  expect(() => signup(user)).toThrow('Name required');
});

DAMP-Styled Test (Self-Contained)

Here, each test declares its exact scenario inline:

test('signup fails with missing email', () => {
  const user = {
    name: 'John Doe',
    email: '',
  };
  expect(() => signup(user)).toThrow('Email required');
});

test('signup fails with missing name', () => {
  const user = {
    name: '',
    email: '[email protected]',
  };
  expect(() => signup(user)).toThrow('Name required');
});

The DAMP version eliminates the need to flip between helpers.js and user.test.js to understand the test conditions.

When Helpers Still Make Sense

If many tests require a valid baseline user, a small, well-named factory can be used after the core tests are stable:

function validUser(overrides = {}) {
  return {
    name: 'John Doe',
    email: '[email protected]',
    ...overrides,
  };
}

// DAMP-style test using the factory for brevity
test('signup succeeds with a complete user', () => {
  const user = validUser();               // clearly a fully valid user
  expect(() => signup(user)).not.toThrow();
});

The helper's purpose remains transparent—"returns a fully valid user"—preserving readability while avoiding repetitive object literals.

Key Files in the Repository

The addyosmani/agent-skills repository contains several files that elaborate on these concepts:

Summary

  • DAMP prioritizes readability over code reuse in test files, making each test self-documenting according to the addyosmani/agent-skills guidelines.
  • Inline test data eliminates indirection, allowing contributors to understand failures by reading a single file.
  • Minimal helpers are acceptable only when they provide clear, transparent value and the test suite is already stable.
  • Production code benefits from DRY, but test code serves as executable documentation that must read like specifications.

Frequently Asked Questions

What does DAMP stand for in software testing?

DAMP stands for Descriptive And Meaningful Phrases. It is a testing principle that advocates for self-contained, readable test code over shared abstractions, ensuring that each test conveys its purpose without requiring navigation to external files.

Should I never use helper functions in test code?

You should keep helpers minimal and localized. According to the addyosmani/agent-skills guidelines in skills/test-driven-development/SKILL.md, helpers become candidates for extraction only after tests are stable, and they should have transparent names like validUser() that make their purpose immediately obvious without reading the implementation.

How do I refactor existing DRY tests to be DAMP?

Start by inlining shared setup data directly into test bodies. Replace abstract factory calls with explicit object literals that show the exact scenario being tested. If shared logic remains, ensure it is well-named and provides a clear "full story" without requiring navigation to other files, as recommended in references/testing-patterns.md.

Is DAMP only for JavaScript tests?

No. While the examples in skills/test-driven-development/SKILL.md use JavaScript, the DAMP principle applies to any language or testing framework. The core concept—prioritizing descriptive, self-contained test stories over shared utilities—is language-agnostic and relevant to Python, Java, Go, and other test suites.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →