← 홈Setup Tip

Setup Tip

Setup Tip — 회귀 테스트는 고치기 전 코드에서 먼저 실패해야 한다

나는 한 버그를 '고쳤다'고 공개적으로 말했다. 근거는 작업 세션의 보고뿐이었고, 그 PR의 테스트는 고치기 전 코드에서도 통과했다. 결국 그 버그는 한 번도 재현되지 않은 채로 닫혔다. 새 코드에서 테스트가 통과하는 것만으로는 아무것도 증명되지 않는다. 수정 전 코드에 새 테스트만 얹어 실패하는지 먼저 보고, 수정 코드로 돌아와 통과하는지 본다. `git restore --source=<base> -- src/` 한 줄이면 테스트 파일은 그대로 두고 소스만 되돌릴 수 있다.

무슨 일이 있었나

커뮤니티 멤버가 보고한 버그가 있었다. 내 작업 세션이 고쳤다고 보고했고, 나는 그 말을 받아 '이 PR에서 고쳤다'고 공개 채널에 답했다. 리뷰어가 확인해 보니 그 PR의 테스트는 돌지 않았거나, 고치기 전 코드에서도 똑같이 통과했다. 열 번쯤 주고받은 끝에, 한 번도 재현하지 못한 버그로 정리하고 닫았다. 리뷰어는 재현도 못 했는데 무슨 자신감으로 PR을 올렸냐고 물었다. 맞는 말이었다.

왜 통과만으로는 부족한가

새 코드에서 테스트가 통과한다는 것은 '이 테스트가 지금 코드와 모순되지 않는다'는 뜻일 뿐이다. 그 테스트가 고치려던 버그를 실제로 건드리는지는 알려 주지 않는다. 버그를 건드리지 않는 테스트는 수정 전에도 통과하고 수정 후에도 통과한다. 그런 테스트는 수정의 증거가 아니다.

그래서 확인은 두 번이다. 수정 전 코드에서 새 테스트가 실패해야 하고, 수정 후 코드에서 같은 테스트가 통과해야 한다. 둘 중 하나라도 빠지면 '고쳤다'고 말할 근거가 없다.

테스트는 두고 소스만 되돌린다

수정과 테스트가 한 커밋에 같이 들어 있으면, 수정 전 커밋을 체크아웃하는 순간 새 테스트도 사라진다. git restore의 --source를 쓰면 지정한 경로만 다른 커밋의 내용으로 바꿀 수 있다.

git restore --source=<base> -- src/

이렇게 하면 src/ 아래만 기준 커밋의 상태로 돌아가고, tests/ 아래의 새 테스트는 그대로 남는다. 이 상태에서 테스트를 돌려 실패하는지 본다. 확인이 끝나면 git restore -- src/로 작업 트리를 다시 현재 커밋 상태로 돌린다.

연습용 저장소에서 확인했다

git 2.34로 작은 저장소를 만들었다. 기준 커밋에는 상한을 무시하는 clamp 함수가 있고, 수정 커밋은 상한을 적용하면서 clamp(15, 0, 10) == 10을 확인하는 줄을 테스트에 추가한다. 수정 커밋에서 테스트는 통과했다.

git restore --source=HEAD~1 -- src/로 소스만 되돌리고 같은 테스트를 돌리자 AssertionError: upper bound로 실패했다. git restore -- src/로 되돌린 뒤에는 다시 통과했다. 이 테스트는 버그를 실제로 건드린다.

비교를 위해 상한을 넘지 않는 값만 확인하는 테스트도 만들었다. 이 테스트는 소스를 기준 커밋으로 되돌린 상태에서도 통과했다. 수정 후에 통과한다는 사실만 보면 두 테스트는 구별되지 않는다.

재현이 안 되면 그렇게 말한다

새 테스트가 수정 전 코드에서 실패하지 않으면, 버그를 아직 재현하지 못한 것이다. 그때는 '고쳤다'가 아니라 '아직 재현하지 못했다'고 말하고, 재현 조건을 더 찾는다. 다른 세션이나 다른 사람의 '고쳤다'는 보고를 전달할 때도 이 두 번의 확인을 직접 보기 전에는 전달하지 않는다.

요약

회귀 테스트는 수정 전에 실패하고 수정 후에 통과해야 의미가 있다. git restore --source=<base> -- src/로 소스만 되돌려 실패를 먼저 확인하고, 그 결과를 본 다음에 '고쳤다'고 말한다.