diff --git "a/05\354\236\245/\354\241\260\355\230\234\353\246\260.md" "b/05\354\236\245/\354\241\260\355\230\234\353\246\260.md" new file mode 100644 index 0000000..e48a121 --- /dev/null +++ "b/05\354\236\245/\354\241\260\355\230\234\353\246\260.md" @@ -0,0 +1,65 @@ +## 5장. 자동 테스트: 좋은 소프트웨어를 만드는 최고의 습관 + +### 1. 자동 테스트의 이점 + +자동 테스트는 기존 코드의 동작을 검증하고, 변경으로 인해 발생할 수 있는 회귀 버그를 발견하는 데 도움을 줌. 또한 테스트를 작성하는 과정에서 코드의 책임과 의존 관계를 파악하게 되므로 유지보수성 향상에도 기여함. 충분한 테스트가 갖춰져 있다면 기존 코드를 수정하거나 리팩터링할 때 변경에 대한 자신감을 높일 수 있음. + +자동 테스트는 동일한 조건에서 반복적으로 실행할 수 있어 코드 변경 이후에도 기존 동작이 유지되는지 지속적으로 검증할 수 있음. 수동으로 반복해서 테스트하는 데 필요한 시간과 노력을 줄이고, 개발 과정에서 빠른 피드백을 제공함. + +책 전반에 걸쳐 테스트의 중요성이 계속 반복해서 등장한다. 처음에는 테스트를 단순히 기능이 정상적으로 동작하는지 검증하는 수단으로 생각했다면, 책을 읽으면서 테스트가 유지보수성과 개발자의 자신감에도 영향을 미친다는 점을 느낀다. +특히 기존 코드를 수정할 때 테스트가 있다면 변경으로 인한 영향을 조금 더 확신을 가지고 검증할 수 있다는 점에서 테스트를 잘 사용해봐야겠다는 생각이 들었다. + +## 2. 자동 테스트 유형 + +### 단위 테스트, 통합 테스트, 종단 간 테스트 + +- **단위 테스트:** 개별 함수나 구성요소의 동작을 독립적으로 검증. 실행 속도가 빠르고 문제 발생 위치를 파악하기 쉬움. +- **통합 테스트:** 여러 구성요소가 연결되었을 때 올바르게 상호작용하는지 검증. +- **종단 간 테스트(E2E):** 실제 사용자 관점에서 애플리케이션의 전체 흐름이 정상적으로 동작하는지 검증. + +각 테스트는 검증 범위와 실행 비용이 다르므로 프로젝트의 특성과 요구사항에 맞게 적절한 비중을 결정해야 함. + +개인적으로는 세 가지 테스트 중 단위 테스트가 가장 중요하지 않을까 생각한다. 개별 기능이 충분히 검증되어 있다면 통합 테스트나 E2E 테스트에서 문제가 발생하더라도 원인을 파악하고 해결하기가 상대적으로 수월할 것 같기 때문이다. 상위 단계에서 발견되는 문제 중 일부도 결국 개별 기능에 대한 검증이 충분하지 않아 발생하는 것은 아닐까 싶다. + +물론 단위 테스트만으로 모든 문제를 발견할 수는 없겠지만, 작은 단위의 동작이 확실하게 검증되어 있어야 그 위에 쌓이는 기능들도 안정적으로 동작할 수 있다고 생각한다. 그렇기에 단위 테스트를 얼마나 충실하게 작성하는지가 전체 테스트의 신뢰성에도 큰 영향을 미칠 것 같다. + +### 테스트하지 말아야 할 것 + +작성한 코드의 모든 부분을 동일한 수준으로 테스트할 필요는 없음. 외부 라이브러리나 프레임워크가 이미 보장하는 동작을 중복해서 테스트하기보다 직접 작성한 코드의 동작에 집중해야 함. 또한 내부 구현 세부사항보다는 외부에서 관찰할 수 있는 동작을 검증하는 것이 바람직함. + +단순히 테스트 수를 늘리기보다 실제로 의미 있는 동작을 검증하는 테스트를 작성하는 것이 중요함. + +## 3. 코드 실행률 + +코드 실행률(Code Coverage)은 전체 코드 중 테스트 과정에서 실행된 코드의 비율을 측정하는 지표. 실행률이 높다고 테스트의 품질이 반드시 높은 것은 아니며, 코드가 실행되었다는 사실만으로 올바른 동작이 검증되었다고 볼 수 없음. + +100% 실행률 달성 자체를 목표로 삼기보다 핵심 로직과 위험 요소를 충분히 검증하고 있는지 확인하는 것이 중요함. + +이 부분을 읽으면서 **코드 실행률은 목적이 아니라 수단이 되어야 한다**는 점을 다시 한번 생각하게 되었다. 결국 테스트의 목적은 높은 수치를 달성하는 것이 아니라 코드가 의도한 대로 동작하는지 검증하는 데 있기 때문이다. + +실행률이라는 수치에만 집중하다 보면 정작 중요한 검증이 이루어지고 있는지는 놓칠 수도 있을 것 같다. 앞으로도 어떤 지표를 활용하든 그 수치 자체를 높이는 것보다 지표를 통해 무엇을 확인하고 개선하려는 것인지 먼저 생각해야겠다고 느꼈다. + +## 4. 테스트 작성 + +### 테스트 우선 개발과 테스트 후 작성 + +- **테스트 우선 개발(TDD):** 구현 전에 실패하는 테스트를 작성하고, 테스트를 통과하는 코드를 구현한 뒤 리팩터링하는 방식. +- **테스트 후 작성:** 기능을 먼저 구현한 다음 동작을 검증하는 테스트를 추가하는 방식. + +두 방식 모두 코드의 동작을 검증한다는 목적을 가지며, 개발 상황과 요구사항에 따라 적절한 방식을 선택할 수 있음. + +테스트를 먼저 만들어놓고, 이후에 구현하는 형식인 테스트 우선 개발(TDD)에 대해서 딱히 생각해본적 없는데 이 방법도 시도해보면 좋을 것 같다는 생각이 들었다. 물론 이 방법이 무조건적으로 좋다고 장담할 순 없지만 테스트를 먼저 만들어놓는다면 내가 구현하는 기능에 대해 계속해서 올바른지 확인하며 구현할 수 있을 것 같아서 한 번 꼭 해봐야겠다는 생각이 든다. + +### 모의 객체와 외부 의존성 분리 + +모의 객체(Mock Object)는 실제 의존 구성요소 대신 사용되는 객체로, 테스트 대상의 동작을 외부 의존성으로부터 분리해 검증할 수 있도록 함. 외부 서비스의 응답을 제어하거나 구성요소 간 상호작용을 검증하는 데 활용할 수 있음. + +개인적으로는 MSW나 Mock을 활용해 테스트하는 방식이 더 효율적이라고 생각한다. 실제 API나 DB에 의존하는 테스트를 작성했다가 외부 환경에 문제가 발생하면, 테스트 대상 코드에는 문제가 없더라도 테스트가 실패할 수 있기 때문이다. 이런 문제를 해결하는 데 불필요한 리소스가 발생할 수 있다는 점에서 외부 의존성을 분리하는 방식이 더 합리적으로 느껴진다. + +다만 실제 API나 DB와의 연결에서 발생하는 문제까지 검증하려면 별도의 통합 테스트가 필요하다는 점도 고려해야 할 것 같다. 결국 모든 테스트에서 실제 구성요소를 사용하는 것보다는 검증하려는 목적에 따라 Mock과 실제 구성요소를 적절히 선택하는 것이 중요하다고 생각한다. + +### 통합 테스트와 종단 간 테스트 작성 + +통합 테스트는 여러 구성요소를 함께 실행해 실제 연결 관계와 상호작용을 검증함. 종단 간 테스트는 HTTP 요청부터 응답과 데이터 처리까지 실제 사용자 흐름에 가까운 방식으로 검증함. + +단위 테스트만으로 확인하기 어려운 구성요소 간 문제를 발견할 수 있지만, 일반적으로 더 많은 실행 시간과 환경 구성이 필요함.