좋은 테스트는 실행되었다는 사실보다 잘못된 구현을 실제로 거절했는지가 중요합니다. 이 노트는 예제와 속성, 독립 oracle, shrinking, 변이 검사를 서로 다른 검출 근거로 구분하고, 환경 실패를 업무 assertion 실패와 섞지 않는 기준을 제시합니다.
테스트 실행과 결함 검출력의 구분
5만원 이상 10% 할인 정책을 예로 들면 49,999·50,000·50,001을 각각 넣어 경계의 기대값을 먼저 정합니다. 새 test가 처음부터 통과하면 그 사실만으로 검출력까지 확인된 것은 아니며, 기존 동작을 다시 확인했거나 assertion이 약할 수 있습니다. Red 단계에서는 구현이 없거나 경계가 틀린 상태에서 의도한 업무 assertion이 실패하는지 확인하고, 문법 오류·모듈 누락 실패는 할인 경계를 잡았다는 증거로 세지 않습니다.
Green은 요구를 만족시키는 최소 구현, Refactor는 결과·예외·상태를 유지한 구조 정리입니다. 미래 기능 추가나 성공 기준 완화와 섞지 않습니다. test 파일을 먼저 만든 사실만으로 Red-Green-Refactor를 수행했다고 하지 않습니다.
제품 함수와 기대값 계산의 오류 공유
테스트가 expected=discount(input)처럼 제품 함수로 기대값을 만들고 같은 함수를 다시 호출하면, 구현이 틀려도 두 결과가 같아 통과할 수 있습니다. 대신 입력·기대 결과를 적은 명시적 예제, 독립 기준 계산, 또는 다른 알고리즘을 사용해 결과를 비교합니다. 내부 private 함수나 불필요한 호출 순서에 결합하지 않되, 외부 효과 횟수처럼 실제 계약인 상호작용은 호출 횟수로 검사할 수 있습니다.
| 검사 | 확인하는 것 | 보장하지 않는 것 |
|---|---|---|
| line coverage | 코드가 실행됨 | assertion의 검출력 |
| 예제 test | 명시한 입력·결과 | 다른 모든 입력 |
| property test | 생성 영역의 불변식 | 수학적 완전 증명 |
| mutation test | 선택한 변이를 거절함 | 요구 자체의 정확성 |
| 통합 test | 실제 외부 계약 | 모든 장애 스케줄 |
정렬 결과의 순서·다중집합·안정성 성질
속성 검사는 먼저 생성 영역과 oracle의 신뢰 경계를 적습니다. 예를 들어 정렬은 isNonDecreasing, 원소별 개수 보존, 필요할 때 동일 key의 순서 보존을 독립적으로 검사하며, 하나의 property가 모두를 대신한다고 가정하지 않습니다.
정렬 결과가 비내림차순이고 입력의 다중집합을 보존해야 합니다. 집합만 같으면 중복 원소 누락을 놓칩니다. 이것만으로 stable sort까지 증명하지 못하므로 같은 key의 원래 순서도 요구라면 추가합니다. encode/decode 왕복·기준 구현 비교·입력 변환에 따른 결과 관계도 유용합니다.
생성기는 빈 값·최대 크기·음수·overflow 경계·중복·정상/비정상 입력을 서로 다른 영역으로 만들어야 합니다. random seed·generator version·실패 입력을 저장한 뒤 shrinking으로 실패를 유지하는 더 작은 사례를 찾아 실패를 재현하기 쉽게 합니다. 줄인 사례를 짧은 회귀 test로 남길 수 있지만, 그것이 운영에서 가장 흔한 입력이거나 절대 최소라는 뜻은 아닙니다.
그림을 글로 읽기
- 명시적 예제·불변식·독립 기준
- 경계 입력·seed·다양한 분포
- 실패를 검출한 assertion
- 실패 유지·입력 축소
- 작은 고정 회귀·구현 수정
- 명시적 예제·불변식·독립 기준 → 경계 입력·seed·다양한 분포: 검사 조건
- 경계 입력·seed·다양한 분포 → 실패를 검출한 assertion: 실제 반례
- 실패를 검출한 assertion → 실패 유지·입력 축소: 원인 좁히기
- 실패 유지·입력 축소 → 작은 고정 회귀·구현 수정: 재현 보존
셔플은 길이·원소 다중집합·제외 대상 같은 결정적 성질과 균등 분포의 알고리즘/통계 근거를 나눕니다. 모든 순열이 유효하다고 균등한 것은 아닙니다. 입력을 두 번 정렬한 결과가 같다는 멱등 성질도 원소를 모두 버리는 잘못된 구현이 통과할 수 있어 여러 성질을 조합해야 합니다.
생존 변이와 제품 결함·동등 변이의 구분
실무에서는 생존한 변이를 먼저 분류합니다. 요구 경계를 놓친 유효 변이면 작은 회귀 예제를 추가하고, 출력·부작용이 실제로 같아지는 동등 변이면 도구의 제외 목록과 근거를 남기며, 컴파일·timeout·환경 오류면 검출력 점수에 넣지 않고 실행 인프라 문제로 처리합니다.
>=50000을 >50000으로 바꾼 변이는 경계 test가 실패해야 합니다. 이처럼 조건·연산을 작은 단위로 자동 바꾼 프로그램을 변이로 만들고, 원래 test suite가 그 변이를 거절하는지 실행하는 것이 mutation testing입니다. 살아남은 변이는 assertion 누락일 수도 있지만 결과가 같아 의미 변화가 없는 동등 변이일 수도 있으며, 컴파일 불가·timeout·환경 실패는 실제 assertion 검출과 따로 분류합니다.
격리한 변이별 실행·시간 한도로 무한 loop를 제어하고 점수만 높이려고 구현 세부에 과결합하지 않습니다. 중요한 불변식의 생존 변이를 조사해 유효한 반례를 회귀로 추가합니다. 이 노트는 검출력 설계이며 실제 mutation 도구를 전 저장소에 실행한 결과는 아닙니다.