테스트는 구현이 아니라 관찰 가능한 계약을 고정한다. 정상 예제뿐 아니라 경계, 실패, 상태 전이를 나눈다.

def clamp(value: int, low: int, high: int) -> int:
    return max(low, min(value, high))

def test_clamp_upper():
    assert clamp(11, 0, 10) == 10

pytest에서는 fixture가 준비와 정리를 맡고, parametrization이 같은 계약의 여러 입력을 표현한다. 예외는 `pytest.raises`로 타입과 메시지를 확인한다.

mock은 객체의 내부 호출 횟수를 무조건 확인하는 도구가 아니다. 외부 API, 시계, 파일 시스템처럼 통제할 경계에 둔다. 내부 함수까지 mock하면 리팩터링만으로 테스트가 깨진다.

버그를 고칠 때 먼저 실패를 재현하는 테스트를 추가하고 수정 뒤 통과시킨다. 커버리지 수치는 실행된 줄을 말할 뿐, 올바른 주장을 했다는 뜻은 아니다. 분기와 불변식을 직접 고른다.

Python 20. 테스트를 설계하는 법의 핵심 흐름도
그림. 테스트는 구현 세부보다 외부에 보이는 계약과 불변식을 고정한다.