80 lines
3.3 KiB
Markdown
80 lines
3.3 KiB
Markdown
---
|
|
name: test-focus-isolation
|
|
description: 'Refactor tests so each one fails for only one reason and asserts one conceptual behavior. Use when: reviewing/refactoring tests with overlapping assertions, duplicate checks, or tests that verify the same behavior as another test.'
|
|
---
|
|
|
|
# Test Focus Isolation
|
|
|
|
## When to Use
|
|
- Two or more tests assert the same outcome (e.g., both check Cola delivery)
|
|
- A test mixes assertions about different conceptual behaviors
|
|
- A failure could break multiple tests for the same root cause
|
|
- Reviewing test coverage and finding overlapping checks
|
|
|
|
## Principles
|
|
|
|
### 1. One Reason to Fail Per Test
|
|
|
|
Each test should fail for **exactly one root cause**. If the same assertion appears in multiple tests, a single bug breaks multiple tests — this wastes debugging time and reduces signal quality.
|
|
|
|
**Rule**: A specific behavior or assertion should appear in exactly **one** test.
|
|
|
|
**✗ Bad** — Both tests fail if Cola delivery breaks:
|
|
```cpp
|
|
TEST_F(suite, cola_delivery) {
|
|
EXPECT_EQ(vm.deliver(Choice::Cola), Brand::Coke);
|
|
}
|
|
|
|
TEST_F(suite, multiple_choices) {
|
|
EXPECT_EQ(vm.deliver(Choice::Cola), Brand::Coke); // ← duplicate
|
|
EXPECT_EQ(vm.deliver(Choice::FizzyOrange), Brand::Fanta);
|
|
}
|
|
```
|
|
|
|
**✓ Good** — Each behavior is tested once:
|
|
```cpp
|
|
TEST_F(suite, cola_delivery) {
|
|
EXPECT_EQ(vm.deliver(Choice::Cola), Brand::Coke);
|
|
}
|
|
|
|
TEST_F(suite, multiple_choices) {
|
|
// Only the NEW behavior this test introduces
|
|
EXPECT_EQ(vm.deliver(Choice::FizzyOrange), Brand::Fanta);
|
|
}
|
|
```
|
|
|
|
### 2. One Conceptual Assert Per Test
|
|
|
|
A test should verify **one conceptual behavior**. If a test checks two different things (e.g., Cola delivery AND FizzyOrange delivery), split it into two tests — unless the concept being tested is specifically "multiple choices work together."
|
|
|
|
**Rule**: Each test name should answer "yes/no" to a single question. The assertions inside should all serve that same question.
|
|
|
|
**✗ Bad** — Two behaviors in one test:
|
|
```cpp
|
|
TEST_F(suite, processes_orders) {
|
|
EXPECT_EQ(vm.deliver(Choice::Cola), Brand::Coke);
|
|
EXPECT_EQ(vm.deliver(Choice::FizzyOrange), Brand::Fanta);
|
|
}
|
|
```
|
|
|
|
**✓ Good** — Each test asks one question:
|
|
```cpp
|
|
TEST_F(suite, delivers_cola) {
|
|
EXPECT_EQ(vm.deliver(Choice::Cola), Brand::Coke);
|
|
}
|
|
|
|
TEST_F(suite, delivers_fizzy_orange) {
|
|
EXPECT_EQ(vm.deliver(Choice::FizzyOrange), Brand::Fanta);
|
|
}
|
|
```
|
|
|
|
**Exception**: If the concept being tested inherently involves multiple values (e.g., "supports multiple choices simultaneously"), it's acceptable to check all of them — because the test name communicates the multi-behavior concept.
|
|
|
|
## Procedure
|
|
|
|
1. **Scan tests for overlapping assertions** — grep for repeated `EXPECT_EQ`, `EXPECT_TRUE`, etc. across tests in the same suite.
|
|
2. **Identify the unique behavior** each test introduces — what does this test verify that no other test verifies?
|
|
3. **Remove duplicate assertions** — keep the assertion only in the test that best owns it. Delete it from the others.
|
|
4. **Rename tests** to clearly describe their singular behavior (e.g., `delivers_cola` instead of `configured_choice_delivers_correct_can`).
|
|
5. **Split tests** if a single test asserts multiple unrelated behaviors (unless the concept is multi-behavior by nature).
|
|
6. **Verify** — run the test suite to ensure coverage is preserved and tests pass. |