////
Search
Duplicate
🎷

Chapter 12. 자동 테스트

자동 테스트에 이야기하기에 앞서 인수 테스트라는 용어를 먼저 살펴보자.
인수 테스트는 시스템이 비즈니스 요구사항을 만족해서 소유권을 넘기기 전에 수행하는 테스트로 인수인계하기 전 마지막으로 검증하는 테스트 단계이다.
인수 테스트 단계의 테스트는 일반적으로 테스트 수행 절차와 기댓값이 적힌 체크리스트로 관리된다.
케이스북을 활용해서 체크리스트를 쉽게 관리할 수 있다.
인수 테스트를 잘못 설계하게 되는 대표적인 경우는 인수 테스트의 대부분을 수동 테스트로 구성하는 것이다.
수동 테스트는 자동 테스트에 비해 비용이 더 많이 발생하기 때문에 품질을 높이기 위해 수행하는 테스트가 비용 증가로 되려 품질이 더 낮아질 수 있다.
이는 테스트 절차를 사람이 직접 수행하고 결과를 눈으로 확인하는 데 많은 시간과 노력이 들기 때문이다.
물론 인수 테스트 과정을 수동 테스트로 채우는 것이 어찌 보면 당연하다. 최종 사용자에게 소프트웨어를 전달하기 전, 마지막 테스트 단계이기 때문에 사용자의 입장에서 테스트를 해볼 필요가 있는 것이다.
그러나 시스템의 모든 테스트가 사람의 손을 반드시 거쳐야만 하는 이유는 없다. 일부는 자동 테스트를 사용해서 충분히 대체될 수 있다.
또한 수동 테스트의 한 가지 문제점은 테스트가 누적된다는 것이다. 시스템에 업데이트가 생겼을 때, 기존의 테스트가 정상적으로 돌아갈 것이라는 보장이 없다. 따라서 기존의 테스트들도 수행해주어야 하기때문에 누적된다.

1. Regression

회귀라는 뜻으로 소프트웨어 개발 분야에서는 시스템에서 정상적으로 제공하던 기능이 어떤 배포 시점을 기준으로 제대로 동작하지 않게 되는 상황을 지칭한다.
즉, 기능 개발이 제대로 되어있지 않던 과거로 회귀했다는 의미로 따라서 Regression은 버그의 일종이며 이때문에 Regression Bug라고 부르는 편이 조금 더 정확하다.
이런 회귀 버그 문제는 테스트를 꼼꼼히 수행함으로써 방지할 수 있으며 이런 목적으로 만들어진 테스트를 회귀 테스트라고 한다.
대부분의 사람들은 새로운 팀에 합류해서 기여를 하고자할 때, 내 기여가 문제가 되진 않을까라는 고민을 한 번씩 해본적 있을 것이다.
이는 대부분의 프로젝트가 성장하면서 암묵적인 약속이나 맥락이 생겨나는 경우가 존재하기 때문으로 프로젝트와 관련이 없던 사람은 이러한 맥락을 모른 채 개발하기 때문이다.
때문에 이런 두려움을 이겨내고 확신을 가져다 줄 수 있는 방법이 시스템적으로 필요한데, 이것이 바로 자동화된 회귀 테스트이다.
회귀 테스트도 중요하지만 더 중요한 것은 그 회귀 테스트가 자동적으로 이뤄져야 한다는 것이다. 회귀 테스트를 사람이 직접하는 것은 비효율적이기 때문이다.
자동화된 회귀 테스트를 프로젝트를 확장하고 유지보수하는 과정에서 적극적으로 활용하자. 기능을 개발, 수정하고 단순히 회귀 테스트를 실행시켜보면 되기 때문이다.

2. 의도

수정된 공통 코드가 처음 만들어졌을 때의 개발 의도를 벗어나게 되는 경우, 의존성을 가진 클래스들이 영향을 받게 된다고 했다.
나아가 이런 문제를 배포 전에 확인할 수 있게 만들어진 것이 회귀 테스트라는 이야기도 했다.
그렇다면 우리는 타인이 작성한 코드에서 의도를 어떻게 이해할 수 있을까?
가장 확실한 방법은 코드 작성자에게 가서 직접 물어보는 방법일 것이다. 이 방법은 원초적이면서도 가장 확실하지만 내가 제어할 수 있는 방법이 아니다.
물론 코드를 읽으며 의도를 파악하는 것도 충분히 가능하다. 하지만 코드를 읽는 것만으로 부족한 경우가 있다.
테스트가 개발자의 의도를 확인하기에 최고의 방법이라고 저자는 말한다.
테스트를 이용하면 클래스에 할당된 책임과 의도를 테스트에 기술할 수 있다. 즉 어떤 객체의 책임과 의도를 테스트에 풀어내는 과정에서 객체에 할당된 책임을 다시 확인할 수 있기 때문이다.
우리가 작성하는 테스트는 주로 클라이언트의 관점에서 객체가 책임을 제대로 수행하고 있는가에만 관심이 있다. 객체에게 고정된 입력을 주고 원하는 출력이 나오는지만 확인한다. 내부 구현은 전혀 신경쓰지 않기 때문이다.
따라서 테스트를 의도를 드러내는 수단으로 바라보는 것이 가능하며 더 나아가 책임을 드러내는 수단으로 활용할 수 있게 된다면 객체지향의 방법론인 책임 주도 설계에 가까워질 수 있다.
더 나아가 테스트는 그 자체로 문서가 될 수 있다. 테스트는 개발자의 의도를 보여주는 수단이며 계약의 내용을 가장 구체적이고 상세히 설명할 수 있는 수단이다. 누구나 클래스에 할당된 책임이 무엇인지 테스트를 통해 확인할 수 있다.

3. 레거시 코드

소프트웨어 분야에서 레거시 코드라는 단어를 흔하게 접해봤을 것이다.
이는 보통 현재의 기술 트렌드나 모범 사례와 동떨어진 코드를 지칭하는데 사용되곤 하며 이와 같은 의견에 대부분의 개발자들은 동의할 것이다.
레거시 코드 활용 전략이라는 책을 쓴 마이클 페더스는 레거시 코드를 테스트 루틴이 없는 코드라고 말했다. 즉 그의 정의에 따르면 작성된 시기에 상관없이 테스트 코드가 없는 코드는 레거시 코드가 되는 것이다.
이 정의는 굉장히 명쾌하면서도 레거시 코드가 가지고 있는 본질적인 문제점을 강조한다.
일반적으로 오래된 코드가 레거시 코드로 여겨지는 이유는 그 코드를 이해, 변경하기 어렵다는 점 때문으로 이 문제는 테스트 코드를 통해 해결될 수 있다.
테스트 루틴이 충분히 갖춰진 코드는 오래됐더라도 안정적이고 변경에 대응하기 쉽다.
이런 사실에 기인해서 리팩토링의 저자 마틴 파울러는 리팩토링을 하기 위해선 반드시 테스트가 동반되어야 한다고 말한다. 그리고 코드를 변경한 결과로 입력에 따른 출력이 달라진다면 이는 리팩토링이 아닌 기능을 변경한 것이라고 한다.