이미지 크기 조정 규칙을 바꿔 짧은 축 비율에 상한을 두는 처리를 넣고 테스트를 돌렸다. 전부 통과했는데 새 규칙은 한 건도 검증되지 않은 상태였다.
Table of contents
Open Table of contents
가짜로 바뀐 검증 대상
테스트 파일을 열어 보니 이미지 처리 라이브러리를 통째로 가짜로 바꿔 놓고 있었다. 그렇게 한 이유는 있었다. 업로드 모듈을 불러올 때 부수 효과가 생겨서 그것을 막으려고 라이브러리를 통째로 대체한 것이었다.
가짜 객체는 크기 조정 함수의 인자를 받고 그냥 버리므로 어떤 값을 넣든 아무 일도 일어나지 않는다. 바뀐 규칙이 어떤 치수를 만들어 내는지 검증할 방법이 애초에 없었고 테스트는 규칙과 무관하게 항상 통과했다. 거기다 실제 변환을 하는 함수는 내보내지지도 않아 직접 호출할 수도 없었다.
가짜와 실제의 구분
고치면서 가짜로 둘 것과 실제로 둘 것을 먼저 갈랐다. 이미지 처리 라이브러리는 검증 대상이므로 실제로 두고, 저장소 클라이언트와 DB와 로그는 검증 대상이 아니고 부수 효과만 내므로 가짜로 뒀다.
실제 변환을 하는 함수를 내보내고 별도 테스트 파일에서 그 함수를 직접 호출해 출력 치수를 실측했다. 같은 방식으로 요청 주소 차단 로직도 내보내 단위 테스트했고 크기 조정 7건과 주소 차단 21건이 추가됐다.
예정을 센 검증 패턴
성격이 비슷한 것을 하나 더 겪었다. DB를 실제로 쓰지 않는 모의 실행 모드를 만들고 로그에 쓰기 흔적이 없는지 확인했는데 특정 테이블 갱신이 731건 매칭돼 모의 실행이 실제로 쓴 것처럼 보였다.
로그를 보니 이렇게 찍혀 있었다.
[DRY-RUN] html src 치환 3건 + content_html UPDATE 예정
검증 패턴이 content_html UPDATE였고 이 문자열은 미리보기 문구 안에 그대로 들어 있다. 패턴이 쓰기를 센 것이 아니라 쓰기 예정을 센 것이다.
패턴을 완료형 표식으로 바꾸고 매칭된 줄을 직접 출력해 그 앞에 모의 실행 표식이 붙어 있는지 확인했다. 패턴별로 개수를 나눠 찍으니 어느 것이 오탐인지 그 자리에서 갈렸다.
두 실패의 공통점
하나는 검증 대상을 없앤 것이고 다른 하나는 검증 조건이 엉뚱한 것을 센 것이다. 둘 다 통과가 나왔고 둘 다 그 통과에 아무 의미가 없었다.
여기서 남은 것은 통과 자체를 못 믿는다는 것이다. 통과가 나오면 한 번 더 물어야 한다. 이 검사가 실패할 수 있는 입력이 존재하는가. 없다면 그것은 검사가 아니라 항상 참인 문장이다.
대조군의 최소 비용
통과가 나오면 일부러 틀린 값을 넣어 본다. 그 입력에도 여전히 통과하면 그 검사는 아무것도 가르지 못하는 것이다. 실패가 나오면 반대로 확실히 맞는 값을 넣어 보고 그래도 실패하면 검사 쪽이 잘못된 것이다.
이 두 번을 돌리는 것이 검사 하나를 신뢰하는 최소 비용이다. 안 돌리면 검사가 있다는 사실만 남고 그 검사의 값은 남지 않는다.
정리
- 부수 효과를 막으려다 검증 대상까지 가짜로 바꾸면 테스트가 항상 통과한다
- 가짜로 둘 것과 실제로 둘 것을 구분하고 검증 대상은 실제로 둔다
- 검증 패턴이 완료가 아니라 예정을 세고 있지 않은지 본다
- 매칭된 줄을 직접 출력하고 패턴별로 개수를 나눠 찍는다
- 통과가 나오면 그 검사가 실패할 수 있는 입력이 있는지 묻는다
- 통과에는 틀린 입력을, 실패에는 맞는 입력을 한 번 더 넣는다
- 대조군을 안 만들면 검사가 있다는 사실만 남는다