---
name: test-behavior-not-implementation
description: "Use when writing, reviewing, or keeping a unit/integration test (Jest, Vitest, xUnit). Detects tests that cannot fail for a defect: mock-only, weak, self-referential, constant-pin, fixture-asserts-fixture."
---

# Test Behavior, Not Implementation

A test calls the code the way its users do and asserts the result they observe against a literal expected value. A test that asserts which calls the code made, or restates a constant the code contains, does neither.

**The check:** before you keep a test, ask whether it would still pass if every function it imports returned `undefined`. If yes, it observes no behavior and cannot fail for a defect. Rewrite the assertion or delete the test.

**Why:** A test that cannot fail for a defect costs CI time and review attention and catches nothing. A constant pin also fails when someone edits the constant or the prompt it restates, so it prevents that edit.

## Five Weak Shapes

- **Weak or no assertion.** No `expect`, or only `toBeDefined`, `toBeTruthy`, `not.toThrow`, `toBeInstanceOf`, `toBeGreaterThan(0)`.
- **Mock or absence only.** Only `toHaveBeenCalled`, `not.toHaveBeenCalled`, `toHaveBeenCalledTimes(N)`, `toBeUndefined`, `toEqual([])`, `toHaveLength(0)`, `not.toBe(wrongValue)`.
- **Self-referential.** The expected value comes from the code under test: `expect(f(a)).toBe(f(a))`, `expect(parsed.url).toBe(buildUrl(...))`.
- **Constant pin.** The assertion restates a hand-maintained constant: `expect(LIMITS.maxTools).toBe(8)`, `expect(PROMPT).toContain("You are")`.
- **Fixture asserts fixture.** The assertion reads data the test built in `beforeEach`, and the subject never runs inside the test body.

## The Fix

**For mock-only:** Assert the payload the mock received or the state after the call, not that it was called.
```typescript
// WRONG
expect(handleClick).toHaveBeenCalledTimes(1)

// RIGHT
expect(handleClick).toHaveBeenCalledWith('item-42')
```

**For weak assertion:** Call the subject inside the test body with one concrete input and assert the literal output or the observable effect.
```typescript
// WRONG
expect(slugify("Hello")).toBeDefined()

// RIGHT
expect(slugify("Hello, World!")).toBe("hello-world")
```

**For self-referential:** Test the mechanism that reads it with one concrete input instead of calling the same function twice.

**For constant pin:** Test the mechanism that reads the constant, not the constant value itself.

**For fixture asserts fixture:** Actually call the subject under test with the fixture as input and assert the output.

When no such assertion exists, delete the test.

## Keep These

- A test of a relation across a table's rows (a key present in two tables, a parent that exists).
- A compile-time check in a `*.test-d.ts` file.
- xUnit: `Assert.NotNull(result)` alone or bare `mock.Verify(...)` are weak — assert the returned value or persisted state instead.
