Programming/Refactoring

테스트 구축하기

Seung-o 2025. 3. 20. 08:05

마틴 파울러의 리팩터링(2판)에서는 코드 구조를 개선하는 리팩터링 기법을 다루지만, 리팩터링을 제대로 수행하려면 반드시 테스트가 필요하다. 이번 글에서는 책에서 다룬 ‘테스트 구축하기’ 챕터를 중심으로 테스트 코드 작성의 핵심 개념을 정리해보았다.

 

리팩터링과 테스트의 관계

리팩터링의 궁극적인 목표는 코드를 더 개선하는 것이지만, 코드의 동작을 보장할 수 없다면 리팩터링 자체가 위험해진다. 이때 필요한 것이 바로 테스트 코드다. 리팩터링을 하기 전에 테스트를 구축하면 코드 변경으로 인한 의도치 않은 동작을 사전에 방지할 수 있다.

테스트 코드가 없다면 리팩터링 과정에서 기존 기능이 망가졌는지 확인하기 어렵다. 따라서 리팩터링을 수행하기 전, 반드시 신뢰할 수 있는 테스트를 작성해야 한다.

 

실패하는 테스트를 직접 확인하자

테스트를 작성할 때, 우리가 기대하는 방식으로 테스트가 동작하는지 확신할 수 있을까? 이를 검증하려면, 실패해야 하는 테스트가 실제로 실패하는지 확인하는 과정이 필요하다.

테스트를 여러 개 작성했음에도 실패하는 경우가 전혀 없다면, 테스트가 실제로 코드를 제대로 검증하고 있는지 의심해야 한다. 이런 문제를 방지하기 위해 일시적으로 코드에 오류를 주입하여 각 테스트가 올바르게 실패하는지 직접 확인하는 것이 좋다.

 

자주 실행하는 테스트 습관 들이기

테스트는 코드 변경 후 최소 몇 분 간격으로 자주 실행하는 것이 바람직하다. 특히 장기간 코드를 수정한 후 테스트를 실행하는 것은 문제가 생겼을 때 원인을 파악하기 어렵게 만든다.

하루에 한 번 전체 테스트를 실행하는 것도 좋은 습관이다. 이를 통해 코드가 예상치 못한 방식으로 변경되지는 않았는지 주기적으로 점검할 수 있다.

 

핵심은 위험한 부분을 테스트하는 것

모든 코드를 테스트할 필요는 없다. 테스트의 목적은 현재 혹은 향후 발생할 수 있는 버그를 찾는 것이므로, 단순히 값을 읽고 쓰는 접근자(Getter, Setter) 같은 부분은 굳이 테스트하지 않아도 된다.

테스트를 너무 많이 작성하면 오히려 중요한 테스트를 놓칠 수도 있다. 따라서 완벽한 테스트를 만들려 하기보다는, 핵심적인 비즈니스 로직이나 버그가 발생할 가능성이 높은 부분을 집중적으로 테스트하는 것이 효과적이다.

(참고: 이와 유사한 내용이 코리 코프의 《단위 테스트》에서도 언급된다. 테스트가 많을수록 리팩터링 내구성은 올라가지만, 무분별한 테스트는 유지보수를 어렵게 만들 수도 있다.)

 

공유 픽스처 문제를 피하자

테스트에서 가장 지저분한 버그 중 하나는 테스트 간의 상호작용으로 인해 발생하는 문제다. 예를 들어, 테스트에서 공유 픽스처(공통 데이터)를 사용하고 이를 변경하는 경우, 이후 실행되는 테스트에 영향을 미칠 수 있다.

이러한 문제를 방지하려면 각 테스트가 독립적인 픽스처를 가지도록 설계해야 한다. Jest나 Mocha 같은 테스트 프레임워크에서 beforeEach를 활용하면, 각 테스트 실행 전에 새로운 상태를 초기화할 수 있다.

 

실전에서는 세터(Setter)도 테스트해야 할까?

일반적으로 세터는 단순한 기능을 하므로 테스트하지 않는 경우가 많다. 하지만 실전에서는 세터가 특정 비즈니스 로직을 수행하는 경우도 존재한다.

책에서도 이런 예제를 다루는데, 만약 세터가 내부적으로 복잡한 계산을 수행하거나 중요한 값을 변경한다면 반드시 테스트해야 한다. 따라서 무조건 ‘세터는 테스트할 필요 없다’는 생각보다는, 코드의 실제 동작을 기준으로 테스트할지를 판단하는 것이 좋다.

 

경계 조건을 고려한 테스트 작성

테스트를 작성할 때는 경계 조건(Boundary Condition)을 고려하는 것이 중요하다. 예를 들어, 배열의 첫 번째 또는 마지막 요소를 처리하는 경우, 숫자의 최소/최대값을 다루는 경우 등을 집중적으로 테스트해야 한다.

다만, 마틴 파울러는 리팩터링을 수행하기 전이라면 경계 테스트를 작성할 필요가 없다고 말한다. 리팩터링은 겉보기 동작(외부 API)을 변경하지 않는 것이 원칙이며, 경계 조건을 다루는 로직이 변경되지 않는다면 리팩터링 시점에서 경계 테스트를 추가하지 않아도 된다는 의미다.

 

테스트에도 수확 체감 법칙이 적용된다

테스트를 너무 많이 작성하면 시간이 지남에 따라 효율성이 떨어질 수 있다. 지나치게 많은 테스트가 부담이 되어 오히려 아무 테스트도 작성하지 않게 되는 상황이 올 수도 있다.

따라서 위험한 부분에 집중해서 테스트를 작성하는 것이 가장 효과적이다. 단순히 코드 커버리지를 100% 맞추는 것보다, 실제로 문제가 발생할 가능성이 높은 부분을 집중적으로 테스트하는 것이 더 실용적이다.

 
수확 체감 법칙?

경제학에서 생산에 필요한 요소들 중 다른 요소들은 변화하지 않은 상태에서 한 생산요소가 증가할때 단위당 한계 생산량은 줄어 드는 현상을 의미한다.
 

리팩터링 이후, 테스트의 중요성은 더욱 커졌다

리팩터링(초판)이 출간된 이후, 개발자들의 테스트에 대한 인식은 크게 개선되었다. 이제는 뛰어난 개발자라면 테스트를 최우선으로 고려하며, 아키텍처를 평가할 때도 테스트 용이성을 주요 기준으로 삼는다.

버그를 발견하는 즉시, 그 버그를 명확히 잡아내는 테스트를 먼저 작성하는 습관을 들이자. 이 습관이 자리 잡으면 리팩터링이 더욱 안전해지고, 코드의 품질도 자연스럽게 향상될 것이다.

 

마무리하며

테스트는 리팩터링과 함께 성장해야 한다. 테스트 없이 리팩터링을 수행하는 것은 안전망 없이 줄타기를 하는 것과 같다. 테스트를 작성할 때는 실패하는 테스트를 확인하고, 위험한 부분에 집중하며, 독립적인 테스트 환경을 유지하는 것이 중요하다.

이제 여러분도 코드 리팩터링을 할 때, 테스트부터 작성하는 습관을 들여보는 것은 어떨까?

'Programming > Refactoring' 카테고리의 다른 글

코드에서 나는 악취  (1) 2025.03.13
리팩터링: 첫 번째 예시  (0) 2025.02.26
리팩터링 2판 공부를 시작하며  (0) 2025.02.24