/nw-test-optimization
Methodology for minimizing test count while maximizing behavioral coverage - behavior definition, anti-pattern catalog, consolidation patterns, stopping criterion, coverage-preserving validation
$ npx -y skills add nWave-ai/nWave --skill nw-test-optimization --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/nw-test-optimization
Context preview
The summary Claude sees to decide when to auto-load this skill.
Methodology for minimizing test count while maximizing behavioral coverage - behavior definition, anti-pattern catalog, consolidation patterns, stopping criterion, coverage-preserving validation
SKILL.md
nw-test-optimization.SKILL.mdname: nw-test-optimization
description: Methodology for minimizing test count while maximizing behavioral coverage - behavior definition, anti-pattern catalog, consolidation patterns, stopping criterion, coverage-preserving validation
user-invocable: false
disable-model-invocation: true
Test Optimization Methodology
Mission
> Minimize tests, maximize value, reduce feedback time, maintain quality. > (Ale, 2026-04-28: "Bisogna minimizzare i test, massimizzare il valore per ridurre il tempo di feedback, mantenendo la qualità.")
This skill operationalizes that mission. Apply during DELIVER COMMIT, scheduled audits, or `/nw-optimize-tests` invocations.
1. Behavior Definition (the loose joint, closed)
The phrase "distinct behavior" in the test budget formula `max_unit_tests = 2 × distinct_behaviors` is the loose joint that lets test counts inflate. Close it with these rules.
1.1 What IS a behavior
A behavior is an **observable outcome via a port** (driving or driven):
- A return value from a driving-port call given specific inputs
- A state change visible through a driving-port query after an action
- A side effect at a driven-port boundary (call sequence + payload)
- An exception raised from a driving port
- A business invariant that holds across a class of inputs
1.2 What is NOT a behavior
- Phrase presence in markdown — that is one **document contract**, not one behavior per phrase
- AST shape (try/except wrapping, decorator presence, import order) — that is source structure, not runtime behavior
- Type system facts (subclass relations, attribute presence) — Python language guarantees, not domain behaviors
- Internal data shape (dataclass field assignment, dict structure) — language guarantees
- Internal method calls (mock.assert_called_with) — implementation, not outcome
- Source-shape compliance — runtime behavior on each supported environment IS the behavior, not the source it was compiled from
1.3 Counting rules — concrete cases
| Surface | Behaviors | |---------|-----------| | Markdown skill with required phrases | 1 (the file conforms to the contract) | | 5 skill files × 30 required phrases each | 5 (one per file), NOT 150 | | Function with N input variations, same assertion shape | 1 (parametrize the variations) | | Function with N error types, distinct messages and paths | N | | Adapter that calls a driven port with ordered payload | 1 per call site, asserted once | | Pure dataclass storing fields | 0 (Python guarantees this) | | ABC with 5 abstract methods | 0 (Python guarantees abstract enforcement) |
1.4 Re-derivation procedure
Before counting tests in a target scope:
1. List the driving ports the scope exposes 2. For each driving port, list the observable outcomes it produces (return values, state, side effects, exceptions) 3. Collapse outcomes that vary only by input value into one parametrized behavior 4. Count the result. That number × 2 = budget.
If your count exceeds 2× behaviors, you have either testing theater or genuinely high behavioral surface — investigate which before adding mass.
2. Banned Anti-Patterns
Each pattern below is an automatic block at review. Counter-example shows the right test.
2.1 Language-Guarantee Tests
Tests that assert what the language already guarantees.
# BANNED — Python @abstractmethod already enforces this at instantiation
def test_config_port_interface_defines_required_methods():
assert issubclass(ConfigPort, ABC)
assert hasattr(ConfigPort, "get_timeout_threshold_default")
# CORRECT — test runtime behavior of a concrete adapter
def test_config_adapter_returns_default_when_unset():
adapter = EnvironmentConfigAdapter(env={})
assert adapter.get_timeout_threshold_default() == DEFAULT_THRESHOLD2.2 AST-Shape Tests
Tests that parse source and assert structural shape.
# BANNED — tests source structure, not runtime behavior
def test_no_bare_typing_self_import():
src = Path("src/des/domain/value_objects.py").read_text()
tree = ast.parse(src)
# ... assert try/except wraps the import ...
# CORRECT — test runtime behavior on each supported version (matrix in CI)
def test_value_objects_import_succeeds_on_python_310():
# Run pytest under Python 3.10 in CI matrix
from des.domain.value_objects import OrderId
assert OrderId("abc").value == "abc"Source compliance is a CI matrix concern, not a unit test.
2.3 Trivial Dataclass-Storage Tests
# BANNED — Python @dataclass guarantees field assignment
def test_turn_limit_config_stores_limits_by_task_type():
config = TurnLimitConfig(quick=20, deep=60)
assert config.quick == 20
assert config.deep == 60
# CORRECT — test the behavior that uses the config
def test_turn_counter_aborts_quick_task_at_limit():
counter = TurnCounter(TurnLimitConfig(quick=20, deep=60))
for _ in range(20):
counter.increment("quick")
assert counter.is_exhausted("quick")If the dataclass has invariants (validation, derived fields), those ARE behaviors — test them.
2.4 Mock-Asserting-Mock
# BANNED — mock returns what you told it to; you are testing unittest.mock
def test_repository_returns_user():
mock_repo = Mock()
mock_repo.get.return_value = User(name="Alice")
result = mock_repo.get(1)
assert result.name == "Alice"
# CORRECT — test the application service that uses the repository
def test_user_service_returns_active_user():
repo = InMemoryUserRepository(users=[User(id=1, name="Alice", active=True)])
service = UserService(repo)
assert service.get_active(1).name == "Alice"2.5 Parametrize-Inflation
One contract becomes N tests by parametrizing every variant.
# BANNED — 150 tests for "the markdown contains required phrases"
@pytest.mark.parametrize("phrase", PHRASES_30)
@pytest.mark.parametrize("skill_dir", SKILLS_5)
def test_skill_contains_phrase(skill_dir, phrase):
md = (skill_dir / "SKRead more
name: nw-test-optimization description: Methodology for minimizing test count while maximizing behavioral coverage - behavior definition, anti-pattern catalog, consolidation patterns, stopping criterion, coverage-preserving validation user-invocable: false disable-model-invocation: true
Test Optimization Methodology
Mission
> Minimize tests, maximize value, reduce feedback time, maintain quality. > (Ale, 2026-04-28: "Bisogna minimizzare i test, massimizzare il valore per ridurre il tempo di feedback, mantenendo la qualità.")
This skill operationalizes that mission. Apply during DELIVER COMMIT, scheduled audits, or `/nw-optimize-tests` invocations.
1. Behavior Definition (the loose joint, closed)
The phrase "distinct behavior" in the test budget formula `max_unit_tests = 2 × distinct_behaviors` is the loose joint that lets test counts inflate. Close it with these rules.
1.1 What IS a behavior
A behavior is an **observable outcome via a port** (driving or driven):
- A return value from a driving-port call given specific inputs
- A state change visible through a driving-port query after an action
- A side effect at a driven-port boundary (call sequence + payload)
- An exception raised from a driving port
- A business invariant that holds across a class of inputs
1.2 What is NOT a behavior
- Phrase presence in markdown — that is one **document contract**, not one behavior per phrase
- AST shape (try/except wrapping, decorator presence, import order) — that is source structure, not runtime behavior
- Type system facts (subclass relations, attribute presence) — Python language guarantees, not domain behaviors
- Internal data shape (dataclass field assignment, dict structure) — language guarantees
- Internal method calls (mock.assert_called_with) — implementation, not outcome
- Source-shape compliance — runtime behavior on each supported environment IS the behavior, not the source it was compiled from
1.3 Counting rules — concrete cases
| Surface | Behaviors | |---------|-----------| | Markdown skill with required phrases | 1 (the file conforms to the contract) | | 5 skill files × 30 required phrases each | 5 (one per file), NOT 150 | | Function with N input variations, same assertion shape | 1 (parametrize the variations) | | Function with N error types, distinct messages and paths | N | | Adapter that calls a driven port with ordered payload | 1 per call site, asserted once | | Pure dataclass storing fields | 0 (Python guarantees this) | | ABC with 5 abstract methods | 0 (Python guarantees abstract enforcement) |
1.4 Re-derivation procedure
Before counting tests in a target scope:
1. List the driving ports the scope exposes 2. For each driving port, list the observable outcomes it produces (return values, state, side effects, exceptions) 3. Collapse outcomes that vary only by input value into one parametrized behavior 4. Count the result. That number × 2 = budget.
If your count exceeds 2× behaviors, you have either testing theater or genuinely high behavioral surface — investigate which before adding mass.
2. Banned Anti-Patterns
Each pattern below is an automatic block at review. Counter-example shows the right test.
2.1 Language-Guarantee Tests
Tests that assert what the language already guarantees.
# BANNED — Python @abstractmethod already enforces this at instantiation
def test_config_port_interface_defines_required_methods():
assert issubclass(ConfigPort, ABC)
assert hasattr(ConfigPort, "get_timeout_threshold_default")
# CORRECT — test runtime behavior of a concrete adapter
def test_config_adapter_returns_default_when_unset():
adapter = EnvironmentConfigAdapter(env={})
assert adapter.get_timeout_threshold_default() == DEFAULT_THRESHOLD2.2 AST-Shape Tests
Tests that parse source and assert structural shape.
# BANNED — tests source structure, not runtime behavior
def test_no_bare_typing_self_import():
src = Path("src/des/domain/value_objects.py").read_text()
tree = ast.parse(src)
# ... assert try/except wraps the import ...
# CORRECT — test runtime behavior on each supported version (matrix in CI)
def test_value_objects_import_succeeds_on_python_310():
# Run pytest under Python 3.10 in CI matrix
from des.domain.value_objects import OrderId
assert OrderId("abc").value == "abc"Source compliance is a CI matrix concern, not a unit test.
2.3 Trivial Dataclass-Storage Tests
# BANNED — Python @dataclass guarantees field assignment
def test_turn_limit_config_stores_limits_by_task_type():
config = TurnLimitConfig(quick=20, deep=60)
assert config.quick == 20
assert config.deep == 60
# CORRECT — test the behavior that uses the config
def test_turn_counter_aborts_quick_task_at_limit():
counter = TurnCounter(TurnLimitConfig(quick=20, deep=60))
for _ in range(20):
counter.increment("quick")
assert counter.is_exhausted("quick")If the dataclass has invariants (validation, derived fields), those ARE behaviors — test them.
2.4 Mock-Asserting-Mock
# BANNED — mock returns what you told it to; you are testing unittest.mock
def test_repository_returns_user():
mock_repo = Mock()
mock_repo.get.return_value = User(name="Alice")
result = mock_repo.get(1)
assert result.name == "Alice"
# CORRECT — test the application service that uses the repository
def test_user_service_returns_active_user():
repo = InMemoryUserRepository(users=[User(id=1, name="Alice", active=True)])
service = UserService(repo)
assert service.get_active(1).name == "Alice"2.5 Parametrize-Inflation
One contract becomes N tests by parametrizing every variant.
# BANNED — 150 tests for "the markdown contains required phrases"
@pytest.mark.parametrize("phrase", PHRASES_30)
@pytest.mark.parametrize("skill_dir", SKILLS_5)
def test_skill_contains_phrase(skill_dir, phrase):
md = (skill_dir / "SKAI agents that guide you from idea to working code, with human judgment at every gate. nWave runs inside Claude Code. It breaks feature delivery into seven waves (discover, diverge, discuss, design, devops, distill, deliver).
Repo: nWave-ai/nWave
Other skills on nwave.
- /nw-ab-critique-dimensions
Review dimensions for validating agent quality - template compliance, safety, testing, and priority validation
Open skill - /nw-abr-critique-dimensions
Review dimensions for validating agent quality - template compliance, safety, testing, and priority validation
Open skill - /nw-ad-critique-dimensions
Review dimensions for acceptance test quality - happy path bias, GWT compliance, business language purity, coverage completeness, walking skeleton user-centricity, priority validation, observable behavior assertions, traceability coverage, and walking skeleton boundary proof
Open skill - /nw-agent-creation-workflow
Detailed 5-phase workflow for creating agents - from requirements analysis through validation and iterative refinement
Open skill - /nw-agent-testing
5-layer testing approach for agent validation including adversarial testing, security validation, and prompt injection resistance
Open skill - /nw-architectural-styles-tradeoffs
Architectural style selection decision matrices, trade-off analysis, structural enforcement rules, and combination patterns. Load when choosing or evaluating architecture styles.
Open skill

