단위 테스트의 목표
Last updated
Last updated
오늘날 대부분의 회사에서 필수로 받아들이고 있다. 특히 기업용 애플리케이션 개발 프로젝트는 거의 모두 단위 테스트를 적용하게 되었다. 오늘날의 단위 테스트에서의 주요 논쟁은 "좋은 단위 테스트란 무엇인가?"인가 이다.
기업용 애플리케이션 : 높은 비니지스 복잡도, 긴 프로젝트 수명, 중간 크기의 데이터, 낮은 수준이나 중간 수준 정도의 성능 요구
일반적으로 단위 테스트를 작성하면 더 좋은 코드 설계로 이어진다. 하지만 단지 좋은 부수 효과일 뿐 주된 목표는 아니이다. 단위 테스트의 목표는 소프트웨어 프로젝트의 지속 가능한 성장을 가능하게 하는 것이다. 아래 사진을 보면 단위 테스트를 적용했을 때와 적용하지 않았을 때의 개발 속도를 비교할 수 있다.
단위 테스트를 적용하는 것은 처음에는 프로젝트를 더디게 할 수 있지만, 단위 테스트를 적용하지 않게 된다면 시간이 지나면서 점점 더 많은 시간을 들여서 개발을 해야한다. 나중에는 결국 개발을 할 수 없는 상황까지 오게된다.\
이렇게 테스트가 없으면 코드의 무질서도는 증가하고 품질도 떨어지며 버그를 양산하여 신뢰도가 감소하게 된다. 제일 안 좋은 것은 안정화가 어렵다는 것이다.
테스트로 이런 경향을 뒤집을 수 있다. 버그에 대한 보험을 제공하는 도구라고 할 수 있으며, 새로운 기능을도입하거나 리팩토링 후에도 기존 기능이 잘 작동하는지 확인하는데 도움이 된다. 결국 테스트를 해야 장기적으로 개발 속도를 유지할 수 있다.
단위 테스트는 리트머스 시험과 같다. 한 방향으로만 동작한다. 즉, 코드가 저품질인지 아닌지는 명확하게 확인 할 수 있지만, 코드가 좋은지 아닌지는 확인할 수 없다. 일반적으로 강결합 상태에서 저품질의 코드가 많지만 결합도가 낮다고 해서 항상 좋은 품질의 코드는 아니다. (한마디로 어떤 것도 좋은 코드인지는 보장하지 않는다. 단지 최악을 피할 수 있게는 도와준다.)
단위 테스트가 프로젝트의 성장에 도움은 주지만 충분하지 않다. 좋은 단위 테스트를 적용해야 한다.
위의 사진을 보면 나쁜 단위 테스트는 프로젝트가 침체하는 시간을 늦춰줄 뿐 궁극적으로 단위테스트를 적용하지 않은 것과 똑같은 결과를 보여준다. 나쁜 테스트 코드는 잘못된 경고를 발생하거나 버그를 찾는데 도움을 주지않고 유지보수를 어렵게 한다.
그리고 개발자들이 테스트 코드를 작성하는 행위에만 몰두하게 한다. 따라서 테스트 코드 작성시 구별없이 작성하지 말고 지속 가능한 프로젝트 성장을 위해서 고품질 테스트에만 집중하는 것이 좋다.
코드를 작성하는 비용과 테스트 코드의 실효성 사이에서 실효성이 더 클 때 작성하는 것이 좋다. 비용은 다음과 같은 상황에서 필요한 시간에 따라 결정된다.\
일반적으로 테스트 커버리지는 높으면 높을 수록 좋다는 생각이 있다. 하지만 완전히 잘못 되었다. 커버리지 또한 괜찮은 부정 지표이지만 좋지 않은 긍정 지표이다.
예를 들어, 코드 커버리지가 10%처럼 너무 낮은 경우에는 테스트 코드 상태가 좋지다는 근거가 될 수 있다. 반대로 100% 커버리지라도 반드시 양질의 테스트는 아닐 수 있다. 테스트 커버리지는 두 가지 지표로 많이 이야기 된다. 코드 커버리지와 분기 커버리지 이다.
위의 IsSTringLong 메소드를 보면 전체 코드는 중괄호를 포함하여 5줄 인걸 확인 할 수 있다. 그리고 Test 메소드는 전체 코드에서 true를 반환하면 4라인을 커버한다. 즉 코드 커버리지는 4/5 = 0.8 = 80%이다. 하지만 아래와 같이 리팩토링하면 웃기는 결과가 발생한다.
메소드의 전체 줄 수는 3줄로 바뀌었지만 로직은 그대로이다. 하지만 코드 커버리지는 3/3 = 1 = 100%로 바뀌었다. 이 예제는 코드 커버리지를 이용해서 얼마나 쉽게 장난을 칠 수 있는 보여줄 수 있는 예이다.
또 다른 커버리지는 분기 커버리지가 있다.
분기 커버리지는 코드 커버리지보다 보다 나은 결과를 보여준다. 분기 커버리지의 지표 if 문과 switch 문과 같이 제어 구조에 중점을 두고있다. 얼마나 많은 케이스들을 고려할 수 있는지를 테스트 하고 있는지 측정한다고 생각하면 된다.
위의 예제를 보면 분기는 1개 가 있고 Test 메소드는 하나의 경우만 테스트 하고 있다. 따라서 분기 테스트 커버리지는 1/2 = 0.5 = 50%이다.
이렇게 보면 분기 커버리지는 믿을만 해 보이지만 그렇지도 않다.
커버리지 지표의 한계는 다음과 같다.
1) 의 내용은 아래의 코드를 보면 WasLastStringLong이 public static 변수로 선언되어 있다. 테스트 코드는 통과하겠지만 WasLastStringLong이 달라진 상황을 모두 테스트했다고 하기 어렵다.
심지어 검증이 없는 테스트 코드도 있다.
이 테스트는 아무런 검증을 하지 않는다. 황당하다고 생각할 수 있지만 실제로 자주 있는 일이다. 그리고 외부 라이브러리를 사용하는 경우에도 커버리지 측정이 어렵다.
위의 코드를 보면 분기 커버리지가 100%인 것을 알 수 있다. 하지만 int.parse 메서드는 .NET 프레임워크에서 제공하는 외부 라이브러리 코드이다.
그리고 이 라이브러리는 아래와 같은 코드 경로를 갖는다. 우리는 이런 코드 경로를 모두 테스트한 것이 아니며, 예측하기도 어렵다.\
커버리지는 100%, 90% 70% 이렇게 목표로 정하는 경우가 있다. 하지만 커버리지 지표는 그 자체로 봐야하는 것이지 목표로 여겨지면 안된다. 이런 목표는 사람들에게 단위 테스트의 목표와 반대되는 동기 부여를 할 수 있다. 테스트 커버리지를 올리기 위해서 테스트 코드를 위한 코드를 작성하고 불필요한 코드를 작성하는 경우가 그렇다. 시스템의 핵심 부분은 커버리지를 높게 두는 것은 좋다. 하지만 높은 수준을 요구 하는 것은 삼가야한다.
무엇이 성공적인 테스트 스위트(테스트 셋)을 만드는가? 아쉽게도 좋은 테스트 스위트인지 판단할 수 있는 방법은 없다. 하지만 성공적인 테스트 스위트는 다음과 같은 특성을 갖는다.
아무리 코드가 변경 될 때마다 아무리 작은 테스트 코드라도 실행해야 한다. 그리고 코드에서 인프라 코드와 도메인 모델과 밀접한 코드들을 주요 테스트 코드 작성 대상으로 삼아야 한다. 그리고 테스트 코드를 작성할 가치가 있는 코드를 작성해야한다.