2018-11-08
오늘 대략 한 6년 만에 쇼킹한 경험을 했다.
구해놓고 아직 읽지는 않은(못한) 'Working Effectively with Legacy Code'의 내용일 것이라 추정되는데, 테스트 코드가 전혀 없고 내용을 거의 모르는 클래스에 대해 Code Coverage의 도움과 함께 Characterization Test를 작성하면서 테스트 코드(라 쓰고 리팩토링을 위한 방어막이라 읽는다)를 만들고, 든든한 테스트 코드를 바탕으로 응집도를 높이고 결합도를 낮추는 방향으로 리팩토링하는 워크샵 수업을 들었다.
생각보다는 상당히 기계적인 과정을 통해 난잡 했던 코드가 요구사항을 그대로 이야기하는 듯한 코드로 변경되는 모습을 보니 이 타이밍에 박수 한 번 쳐야겠다 생각이 들었지만, 웃기게도 나만 그런 느낌을 받은 모양이라 박수는 삼갔다. ㅋㅋ
개인적으로 테스트 코드의 필요성, TDD의 효율성에 대해서는 의문을 가질 필요가 없다고 생각한다. 그렇다고 내가 그걸 제대로 한다는 소리는 아니고 ㅋㅋ.
테스트 코드 작성이나 리팩터링, TDD(셋은 서로 많이 다르기도 하지만 일단 그냥 하나로 퉁쳐서..)가 현실적으로는 그다지 유효하지 못하다고 얘기하는 이유는 결국은 '속도'가 따라주지 못하기 떄문이라고 생각한다. 그 '속도'를 올리는 방법을 알기가 막막해서 여러번 좌절해왔는데 오늘 그 '속도' 문제를 해결하는 소중한 힌트를 얻을 수 있는 광경을 눈 앞에서 실제로 봤다.
먼저 Characterization Test.
테스트 대상 클래스(CUT, Class Unter Test 라 하자)에 대한 사전 지식이 전혀 없는 상태에서 CUT의 생성자를 시작점으로 해서 테스트 코드를 작성하고 코드 커버리지를 소폭 늘리고, 조건/반복문 등을 만나면 해당 조건을 충족하는 테스트 코드를 작성하고 테스트 한 후, actual 값과 expected 값을 비교하는 테스트가 실패하면 actual 값을 expected 에 덮어 쓰면서 테스트를 통과 시키고 커버리지를 늘리고, CUT의 다음 코드에 대한 테스트 코드를 작성하고 테스트 한 후, 테스트가 실패하면 actual 값을 expected 에 덮어 쓰면서 테스트를 통과 시키고 커버리지를 늘리고.. 이를 반복하면서 코드 커버리지를 100%로 만들면 CUT에 대한 이해도 증진과 함께 리팩토링을 위한 방머막이 테스트 코드로 남게되는 엄청난 결과를 얻게 된다.
물론 단순히 코드 커버리지가 100%로 나온다고 해서 테스트 코드가 CUT의 모든 기능을 포괄한다고 단정할 수는 없다. 가장 대표적인 예가 반복문이다. 반복문 내의 코드는 단 한 번씩만 실행되어도 코드 커버리지는 100%로 나온다. 하지만 반복문은 그 내부 코드가 여러 번 반복 실행될 떄도 정상 동작해야 하므로, 반복문 내부를 여러 번 실행할 수 있는 테스트 코드가 필요하다.
Characterization Test를 통해 CUT의 실질적인 코드 커버리지가 100% 가 되면 드디어 기존 동작의 정상 동작을 보장해주는 방어막이 완성되고, 리팩토링 할 준비가 된다. 이 과정에서 중요한 것은 역시 '속도'인데 IDE의 리팩토링 메뉴에 있는 기능들이 속도를 높이는 데 아주 중요한 역할을 한다. 개인적으로 지금까지는 메서드 추출, 이름 변경 정도나 단축키로 써왔는데 인라인화, 변수화, 메서드 시그니처 변경, 코드 위/아래 이동, 다른 클래스로 코드 이동 등을 단축키로 진행하면 속도를 상당히 높일 수 있다.