4.8 KiB
4.8 KiB
Unit Testing
📌 Brief Summary
유닛 테스트(Unit Testing)는 테스트 자동화 피라미드의 기반을 형성하며 안전한 리팩토링을 수행하기 위한 필수적인 전제 조건입니다 [1-3]. 이는 개별 컴포넌트나 함수의 동작을 격리하여 검증하는 작고 빠른 독립적인 테스트를 의미합니다 [1, 4]. 실행 가능한 명세서이자 가드레일 역할을 함으로써, 개발자가 코드의 외부 동작을 변경하지 않고 내부 구조를 개선할 때 발생할 수 있는 결함을 방지하고 즉각적인 피드백을 제공합니다 [1, 5, 6].
📖 Core Content
- 유닛 테스트의 정의와 특성: 유닛 테스트는 코드베이스에서 가장 좁은 범위를 다루며, 특정 컴포넌트나 메서드가 의도한 대로 동작하는지 확인합니다 [4]. 마이클 페더스(Michael Feathers)는 실행 속도가 너무 느리거나(예: 테스트당 100ms 초과), 데이터베이스, 네트워크, 파일 시스템 등 외부 인프라와 통신하는 테스트는 진정한 의미의 유닛 테스트가 아니라고 정의했습니다 [4, 7, 8].
- 리팩토링 과정에서의 필수 역할: 리팩토링을 시작하기 전에는 반드시 코드가 자가 테스트(Self-testing)가 가능하도록 견고한 유닛 테스트 스위트를 구축해야 합니다 [2, 3, 6]. 테스트 주도 개발(TDD)의 'Red-Green-Refactor' 원칙에 따라, 테스트는 비즈니스 로직을 변경하지 않고 최적화를 진행할 수 있게 하는 안전망 역할을 수행합니다 [9-12]. 또한 버그가 보고되었을 때는 해당 버그를 드러내는 유닛 테스트를 먼저 작성한 후 수정하는 것이 모범 사례입니다 [13, 14].
- 테스트 피라미드와 실행 속도: 유닛 테스트는 작성과 유지보수 비용이 저렴하고 밀리초 단위로 실행되기 때문에 테스트 피라미드의 최하단을 구성해야 합니다 [1]. 건강한 테스트 스위트라면 전체 자동화 테스트의 약 70%가 유닛 테스트로 이루어져야 하며, 강력한 격리를 통해 수 분 내에 수천 개의 테스트를 실행할 수 있어야 합니다 [1, 15].
- 고립형(Solitary) vs 사교형(Sociable) 테스트: 외부 의존성으로 인한 부수 효과를 피하기 위해 목(Mock)이나 스텁(Stub)과 같은 테스트 대역(Test Double)을 사용하여 격리하는 '고립형' 테스트와, 실제 협력 객체를 그대로 사용하는 '사교형' 테스트 방식이 모두 활용됩니다 [16-18].
- 구조와 설계 모범 사례: 유닛 테스트는 내부 구현 세부 사항이 아니라 클래스의 '퍼블릭 인터페이스'와 '관측 가능한 동작(Observable Behaviour)'을 테스트하는 데 집중해야 합니다 [19, 20]. 또한 테스트 코드 작성 시에는 데이터 설정(Arrange), 대상 메서드 호출(Act), 결과 검증(Assert)의 3단계 구조를 따르는 것이 가독성과 일관성을 높여줍니다 [21].
⚖️ Trade-offs & Caveats
- 구현에 대한 과도한 결합(Brittle Tests): 유닛 테스트가 내부 코드 구조나 프라이빗 메서드(Private Method)의 구현에 너무 밀접하게 결합되면, 리팩토링을 수행할 때마다 테스트가 깨지게 됩니다 [19, 22, 23]. 내부 구현이 바뀔 때마다 실패하는 테스트는 유지보수의 부담을 가중시키고 리팩토링의 안전망이라는 본래 목적을 훼손하여 개발자의 피로도를 높입니다 [20].
- 단순 코드 테스트 및 100% 커버리지 추구의 함정: 단순한 Getter/Setter나 복잡한 조건 로직이 없는 사소한 코드까지 모두 테스트하여 100% 커버리지를 달성하려는 노력은 시간 낭비이며 수확 체감의 법칙에 직면하게 됩니다 [21, 24-26]. 테스트는 결함이 발생할 가능성이 있는 위험성과 복잡성이 높은 영역에 집중되어야 합니다 [25, 27].
- 통합 문제 발견의 한계: 유닛 테스트는 개별 컴포넌트의 동작만 격리하여 확인하므로, 독립적으로는 올바른 컴포넌트들이 서로 상호작용할 때 발생하는 결함은 잡아낼 수 없습니다 [28]. 따라서 유닛 테스트에만 의존하면 안 되며, 통합 테스트(Integration Tests)나 E2E 테스트 등 상위 계층의 테스트를 병행하여 실제 환경에서의 문제를 예방해야 합니다 [28-30].
- AI 생성 테스트의 한계: 대규모 언어 모델(LLM)을 활용하여 유닛 테스트를 자동 생성할 경우, 테스트 커버리지의 한계에 부딪히거나 모킹(Mocking) 기능이 부족하고 테스트 코드가 쉽게 깨지는(brittleness) 부작용이 발생할 수 있으므로, 적절한 프롬프팅 기법과 개발자의 철저한 검토가 동반되어야 합니다 [5, 31, 32].
Last updated: 2026-05-03