← Testing and quality guides

How to test negative constraints

How-To Testing and quality Intermediate 1131009HOWTO-1131009

HOWTO-1131009Testing and qualityIntermediate

This guide shows you how to test negative constraints in spec-driven development.

Negative constraints are rules about what must not happen. In SpecDD, they usually appear in Must not and Forbids. They protect boundaries, non-goals, forbidden behavior, blocked dependencies, paths, tools, modules, libraries, and access patterns.

Negative constraints are extremely useful, but they require judgment. Some deserve unit or integration tests. Some are better enforced with static checks. Some are best covered by code review because automation would be brittle or misleading.

Short answer

Classify the negative rule first. Use behavior tests for Must not rules that describe observable forbidden behavior. Use static checks, dependency rules, or linting for Forbids entries that block imports, paths, modules, libraries, or access patterns. Use review evidence for judgment-heavy boundaries. Do not force every negative constraint into a unit test.

When to use this guide

Use this guide when:

Steps

1. Classify the negative rule

Behavior-level Must not:

Must not:
  Save itinerary items after validation fails.

Dependency-level Forbids:

Forbids:
  UI importing ../adapters/*

Policy or review-level boundary:

Must not:
  Change destination search ranking.

The right evidence depends on which kind of rule you have.

2. Test forbidden behavior

For observable forbidden behavior, write a test:

Must not:
  Save itinerary items after validation fails.

Possible test:

Validation failure does not call storage and leaves the itinerary unchanged.

This test proves a specific negative effect.

3. Test forbidden side effects

Negative constraints often protect side effects:

Must not:
  Emit TripDataExported when export authorization fails.
  Send notification emails before trip edit access is confirmed.
  Retry permission failures.

Possible checks:

Use mocks, fakes, spies, logs, or integration harnesses only when they are part of the project’s normal testing style.

4. Check forbidden dependencies statically

For Forbids:

Forbids:
  domain importing ../adapters/*
  UI importing ../adapters/*

A static check is usually better than a runtime test:

Runtime tests rarely prove that a forbidden import cannot be added later.

5. Add regression guards for known failures

If a forbidden behavior already regressed once, add a focused guard:

Scenario: validation failure does not store
  Given the place name is empty
  When the person adds the itinerary item
  Then validation fails
  And no itinerary item is stored

This scenario can become a regression test.

6. Use review evidence where automation is not practical

Some negative rules are important but hard to automate cheaply:

Must not:
  Change destination search ranking.

Possible evidence:

If a rule keeps regressing, invest in stronger automation later.

7. Put the evidence in Done when

Good:

Done when:
  Validation failure does not call storage.
  UI import boundary is covered by the import-boundary check.
  Destination search ranking remains unchanged by review and existing checks.

Weak:

Done when:
  Negative cases pass.

Name the evidence that matters.

Examples

Must not behavior

Must not:
  Save itinerary changes after validation fails.

Done when:
  Validation failure does not call storage.

Best evidence: focused behavior test.

Forbids dependency

Forbids:
  UI importing ../adapters/*

Done when:
  UI import boundary check passes.

Best evidence: static import-boundary check.

Boundary protected by review

Must not:
  Change destination search ranking.

Done when:
  Existing destination search checks pass.
  Review confirms destination ranking inputs are unchanged.

Best evidence: existing tests plus targeted review, unless this boundary keeps regressing.

Common mistakes

How to verify the result

Negative constraints are covered well when:

← Testing and quality guides