# One Test One Thing

> Use when writing or reviewing a unit/integration test — enforces a clear scenario-shaped name (no `_and_`), Arrange/Act/Assert structure with visible separation, and one logical behaviour per test. A failing test name should describe exactly what regressed without opening the file. Multiple scenarios become multiple tests, not one test with branches or `_and_` in the name.

- Skill: `amitkot/one-test-one-thing` (Agent Skill)
- Install (CLI): `npx skillmds@latest add amitkot/one-test-one-thing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/amitkot/one-test-one-thing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: amitkot (https://skillmd.com/u/amitkot)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/amitkot/one-test-one-thing

---


# One test, one thing — name it for the scenario, structure it AAA

Each test exercises one logical behaviour, is named after the exact scenario and expected outcome, and is laid out Arrange / Act / Assert. If the name needs `_and_`, it's two tests.

## Why

A test name is the failure message in CI. When `test_x_and_y` goes red, you can't tell which behaviour broke without opening the file — and the test itself often half-passes through one branch and half-asserts the other, hiding regressions. One scenario per test means the name pins the regression, the AAA shape makes the intent scannable, and a future reader fixes the right thing instead of patching whichever assert tripped first.

## How to apply

- **Name = scenario + expected outcome.** `<unit>_<condition>_<expected>`. If you wrote `_and_`, split the test.
- **AAA visible.** Pick blank-line separation OR `// arrange` / `// act` / `// assert` comments — either works, just make the three phases readable at a glance.
- **One thing.** One logical behaviour per test. Multiple `assert!` calls are fine when they all check the same behaviour from different angles (e.g. asserting `result.foo` and `result.bar` after one call). Multiple distinct scenarios → multiple tests.

## Examples

The "and" smell (live case):

```rust
// BEFORE — one test, two scenarios, "and" in the name
#[test]
fn group_len_returns_expected_size_and_zero_for_unknown_group() { ... }

// AFTER — split into two
#[test]
fn group_len_returns_member_count_for_known_group() { ... }

#[test]
fn group_len_returns_zero_for_unknown_group() { ... }
```

Packed-scenarios smell (live case): `created_group_flag_is_true_only_for_first_order_in_a_new_group` exercised three states (new group, same group, different group) in one method — split into three named tests, one per scenario.

AAA shape on a small Rust test:

```rust
#[test]
fn group_len_returns_zero_for_unknown_group() {
    // arrange
    let store = GroupStore::new();

    // act
    let len = store.group_len(&GroupId::from(42));

    // assert
    assert_eq!(len, 0);
}
```

