# Contract Driven Testing

> Derive a three-level test pyramid directly from a function's Code Contracts (preconditions, postconditions, invariants) — contract-targeted unit tests, property-based tests, precondition-violation tests, plus the no-contracts fallback and the contract review checklist. Loaded by the code-critic agent.

- Skill: `bobmatnyc/contract-driven-testing` (Agent Skill)
- Install (CLI): `npx skillmds@latest add bobmatnyc/contract-driven-testing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bobmatnyc/contract-driven-testing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: bobmatnyc (https://skillmd.com/u/bobmatnyc)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bobmatnyc/contract-driven-testing

---


# Contract-Driven Testing

When a function under review carries Code Contracts (preconditions,
postconditions, invariants — see the project's `## Code Contracts`
convention), derive a three-level testing pyramid directly from them. **The
contracts ARE the test plan** — this is a mechanical translation, not a
creative exercise.

The examples below show the same pyramid in three ecosystems (Python +
`icontract`/`hypothesis`, Rust + `quickcheck`/`proptest`, TypeScript +
`fast-check`). Use whichever matches the project under review; the structure
generalizes to any language with an assertion library and a property-testing
framework.

## Level 1 — Contract-Targeted Unit Tests

- Read every precondition, postcondition, and invariant on the function under
  test.
- Write one focused test per postcondition. Derive the test name from the
  postcondition itself so the intent is legible without reading the body.
- Cover both the happy path and the boundary conditions for each
  postcondition.

**Python:**
```python
# Given postcondition: len(result) == min(k, len(items))
def test_top_k_result_length_equals_min_k_items():
    assert len(top_k([1.0, 2.0, 3.0], k=2)) == 2   # k < len
    assert len(top_k([1.0, 2.0, 3.0], k=10)) == 3  # k > len
```

**Rust:**
```rust
// Given postcondition: result.len() == k.min(items.len())
#[test]
fn top_k_result_length_equals_min_k_items() {
    assert_eq!(top_k(&[1.0, 2.0, 3.0], 2).len(), 2);   // k < len
    assert_eq!(top_k(&[1.0, 2.0, 3.0], 10).len(), 3);  // k > len
}
```

**TypeScript:**
```typescript
// Given postcondition: result.length === Math.min(k, items.length)
test("top_k result length equals min(k, items.length)", () => {
  expect(topK([1.0, 2.0, 3.0], 2)).toHaveLength(2);   // k < len
  expect(topK([1.0, 2.0, 3.0], 10)).toHaveLength(3);  // k > len
});
```

## Level 2 — Property-Based Tests from Contracts

- Translate each postcondition into a property assertion that holds for
  *every* generated input, not just the hand-picked examples in Level 1.
- Encode preconditions as generator constraints — constrain what the
  generator produces, don't filter/reject after the fact (rejection sampling
  wastes cases and can hide precondition bugs).
- Use the property-testing framework native to the stack: `hypothesis`
  (Python), `fast-check` (TypeScript/JavaScript), `quickcheck` or `proptest`
  (Rust), or the equivalent for the language under review.

**Python (hypothesis):**
```python
from hypothesis import given
import hypothesis.strategies as st

@given(
    items=st.lists(st.floats(allow_nan=False), min_size=1),  # encodes: len(items) > 0
    k=st.integers(min_value=1),                              # encodes: k > 0
)
def test_top_k_all_postconditions(items, k):
    result = top_k(items, k)
    assert len(result) <= len(items)                  # postcondition 1
    assert len(result) == min(k, len(items))          # postcondition 2
    assert all(x in items for x in result)            # postcondition 3
```

**Rust (proptest):**
```rust
proptest! {
    #[test]
    fn top_k_all_postconditions(
        items in prop::collection::vec(any::<f64>(), 1..50), // len(items) > 0
        k in 1usize..20,                                      // k > 0
    ) {
        let result = top_k(&items, k);
        prop_assert!(result.len() <= items.len());            // postcondition 1
        prop_assert_eq!(result.len(), k.min(items.len()));    // postcondition 2
        prop_assert!(result.iter().all(|x| items.contains(x))); // postcondition 3
    }
}
```

**TypeScript (fast-check):**
```typescript
import fc from "fast-check";

test("topK satisfies all postconditions", () => {
  fc.assert(
    fc.property(
      fc.array(fc.double(), { minLength: 1 }), // len(items) > 0
      fc.integer({ min: 1 }),                  // k > 0
      (items, k) => {
        const result = topK(items, k);
        expect(result.length).toBeLessThanOrEqual(items.length); // postcondition 1
        expect(result.length).toBe(Math.min(k, items.length));   // postcondition 2
        expect(result.every((x) => items.includes(x))).toBe(true); // postcondition 3
      },
    ),
  );
});
```

**Tip:** where the language's contract library supports it (e.g. Python's
`icontract-hypothesis`), auto-generate strategies directly from the contract
decorators instead of hand-writing the generator constraints.

## Level 3 — Precondition Violation Tests

- Write one negative test per distinct precondition.
- Verify the contract fails loudly with a useful message — not a silent
  wrong answer, not a generic panic with no context.
- Verify that valid inputs never trigger a spurious violation (a
  false-positive precondition check is its own bug).

Assert on the contract library's violation/failure type for the language
under review — `icontract.ViolationError` in Python, a `panic!`/`Result::Err`
from a `debug_assert!`/contract macro in Rust, a thrown error from the
contract wrapper in TypeScript. The structure generalizes across all three;
only the exception/assertion mechanism changes.

**Python:**
```python
def test_top_k_rejects_empty_items():
    with pytest.raises(icontract.ViolationError, match=r"len\(items\) > 0"):
        top_k([], k=1)

def test_top_k_rejects_nonpositive_k():
    with pytest.raises(icontract.ViolationError):
        top_k([1.0, 2.0], k=0)
```

**Rust:**
```rust
#[test]
#[should_panic(expected = "items must be non-empty")]
fn top_k_rejects_empty_items() {
    top_k(&[], 1);
}

#[test]
#[should_panic(expected = "k must be positive")]
fn top_k_rejects_nonpositive_k() {
    top_k(&[1.0, 2.0], 0);
}
```

**TypeScript:**
```typescript
test("topK throws on empty items", () => {
  expect(() => topK([], 1)).toThrow(/items must be non-empty/);
});

test("topK throws on non-positive k", () => {
  expect(() => topK([1.0, 2.0], 0)).toThrow(/k must be positive/);
});
```

## When There Are No Contracts (Yet)

If the function under review has no explicit contracts:

1. Flag it for the engineer if the function is complex or safety/security
   critical — contracts should have been written alongside the
   implementation, not after.
2. Infer implicit contracts from the function's docstring/doc comment and its
   observable behavior.
3. Write property-based tests against the inferred properties anyway — the
   absence of a formal contract doesn't excuse the absence of a test.

## Contract Review Checklist

When reviewing contracts written by the engineer:

- [ ] Postconditions on return-value functions reference the result;
      postconditions on mutating functions reference the mutated object.
- [ ] Relational postconditions are present (ordering, identity, structural
      relationship to inputs) — not just "a result exists."
- [ ] No side effects in contract expressions (contracts must be pure).
- [ ] Pre-call state is captured (`old()` or the language's equivalent) where
      a mutation postcondition needs to reference it.
- [ ] Every precondition has a corresponding Level 3 violation test.

