테스트 커버리지 100% - SLASH 21
Last updated
Last updated
테스트 커버리지를 70% 정도면 적정하다고 생각해왔다.
그런데 클린코드에서 아래와 같이 문구가 있었다.
도저히 테스트하기 힘든 부분도 있고, 코드양도 너무 많을거라 생각했다. 하지만 막연히 말도 안된다고 생각하지 않고 실제로 해보기로 했다.
의외로 해보니 가능했다.
가능했지만 유지가 가능한지도 궁금해졌다.
다행히 1년 6개월간 유지할 수 있었다.
그리고 자신있게 배포를 누를 수 있게 되었다.
덕분에 거침없이 리팩토링을 할 수 있게 되었다. 리팩토링에 문제가 있다면 테스트가 알려줄 것이기 때문이다.
불필요한 프로덕션 코드가 사라진다. 남아 잇다면 그것 또한 테스트해야 한다.
그리고 프로덕션 코드에 대한 이해도가 상상했다. 이해하지 못하면 테스트 작성이 어렵기 때문이다.
점점 테스트 작성이 쉬워졌다. 이미 작성한 테스트를 참고해서 새로운 테스트를 작성할 수 있었다.
테스트 커버리지를 올리기 위해서 필요한 것은 무엇일까? 믿음과 시간이다.
테스트가 필요하다는 믿음이 필요하다.
테스트가 없으면 리팩토링 못하고, 리팩토링 못하면 코드를 이해할 수 없으며, 이해할 수 없는 코드는 수정할 수 없어진다고 믿었다.
어떤 코드든 테스트할 수 있다는 믿음이 필요하다. 조금만 테스트가 어려운 코드를 만나도 테스트를 포기할 것이다.
믿음이 있어도 시간이 있어야 가능하다. 프로젝트 초기부터 커버리지를 높게 유지하는 것이 중요하다.
필요하지 않은 것은 의지이다.
무슨 말인가? 인간의 의지는 도구가 대신할 수 있다. Gradle의 JaCoCo 플러그인에 들어 있는 JacocoTestCoverageVerification은 테스트 커버리지 수치에 따라 빌드를 실패시킨다.
테스트의 개수가 늘어갈 수록 테스트 실행 시간이 오래 걸리기 시작했다. (테스트 400개일 때 기다리는 시간 1분) 전체 테스트를 확인할 때마다 1분 기다려야했다. 그래서 스프링 애플리케이션 컨텍스트 로딩을 제거했다. WebTestClient는 컨텍스트 로딩 없이 테스트가 가능하다.
또 테스트가 1600개를 넘어서니 다시 실행시간에 1분이 초과되었다. 느려지는 원인을 찾으려면 프로파일링이 필요했다.
돈을 써서 해결하기도 했다. 더 좋은 노트북 삼ㅎㅎ
커버리지 기준이 라인 커버러지가 아닌 인스트럭션 커버리지 였기 때문.
커버리지 검사도구에서 그 파일을 제외해야하는 경우도 있다.
왜 이렇게 100%에 집착했나?
100%는 단순하다. 커버가 안되면 빌드가 실패하고, 그 곳을 확인해서 테스트를 작성하면 된다.
99%는 복잡하다. 운 좋게 넘어갈 수도, 억울하게 실패할 수도 있다.
100% 커버리지를 만들어도 버그는 있다.
테스트를 잘못 작성하는 경우
요구 사항에 오해가 생긴 경우
컴포넌트간 협업에 실패한 경우
위 케이스에서는 sum을 + 연산자를 내부에서 실행하더라도, - 연산자를 내부에서 실행하더라도 테스트 커버리지 100%를 달성하게 될 것이다. 이런 문제는 어떻게 회피할까?
뮤테이션 테스팅이라는 기법이 있다.
프로덕션 코드를 무작위로 조작하고 테스트가 통과하면 테스트 케이스가 부족한 것이다.
Pitest를 이용하면 뮤테이션 테스팅을 하여 부족한 테스트를 잡아내준다. 하지만 굉장히 느리다는 단점을 가지고 있다. 그러므로 중요한 로직에만 부분적으로 적용하는 것이 좋다. (도입하지 못함)
요구 사항을 작성하는 사람이 테스트를 작성하는 것이 좋지만 그게 어렵다. 테스트를 작성하면 스펙 문서가 작성되도록 해보았다. TestExecutionListener를 이용해서 마크다운 형식으로 된 스펙 문서를 생성하도록 했다. 그런데 계속 지속하지는 못했다. 스펙 문서를 잘 작성하려면 테스트를 작성할 때 상당한 노력이 필요했기 때문이다. 문제해결을 계속 고민하고 있다.
계약을 관리해주는 Pactflow라는 도구가 존재한다.