이지은님의 블로그
250228 - 테스트와 테스트코드의 중요성(단위테스트, 통합테스트, 테스트코드의 작성방법) 본문
▷ 오늘 배운 것
테스트 코드의 중요성에 대해서 TIL을 작성해보고자 한다.
<<목차>>
1. 테스트의 중요성
1) 테스트가 중요한 이유
2) 테스트 코드의 필요성
2. 테스트 코드 작성의 원칙
1) FIRST 원칙
2) 테스팅 7원칙
3. 테스트의 종류
1) 단위 테스트 (Unit Test)
2) 통합 테스트 (Integration Test)
4. 테스트 코드는 어떻게 작성해야할까?
1. 테스트의 중요성
1) 테스트가 중요한 이유
=> 기능이 요구사항대로 잘 동작하고 있는지 확인하기 위해서
테스트 방법
- 테스트코드를 만들어 테스트 진행.
- Swagger, Postman등을 통해 여러 케이스들을 확인해보며 테스트를 진행.
- QA 과정을 통해 UI, Front, Server 까지 한번에 테스트
2) 테스트 코드의 필요성
1️⃣ 버그를 조기에 발견 할 수 있다.
- 테스트 코드를 작성했다면 버그들을 개발 단계에서 미리 확인하고 수정할 수 있다.
2️⃣ 코드 품질을 개선 할 수 있다.
- 테스트 코드를 작성하기 어려운 기능들이 존재하는데 테스트 코드를 쉽게 만들기 위해 서비스 코드를 개선하게 된다.
- 클래스의 역할과 책임에 대해 고민해보게 되고 클린 코드를 작성하는데 많은 도움을 준다.
3️⃣ 배포단계에서의 안정성을 보장해준다.
- CICD(지속적 통합, 지속적 배포)가 적용이 되어있는 프로젝트는 특정 Branch에 Merge를 할때 마다 자동으로 서버 배포가 진행이 되는데 테스트 코드가 없다면 기능에 문제가 없는지 확실한 보장을 못해준다.
4️⃣ 코드 리팩토링이 쉬워지고 개선 하는것에 주저함이 없어진다.
- 현재 운용중인 코드의 성능 개선 또는 가독성/구조 개선 차원에서 리팩토링을 진행하려고 할때
테스트 코드가 존재하지 않다면 변경한 코드들이 기존과 같은 동작을 하는지 확신이 없어 모든 상황을 테스트 해야한다.
5️⃣ 시간이 지날 수록 테스트를 해야하는 기능들이 많아진다.
- 서비스가 발전함에 따라 추가/변경되는 기능들이 너무 많아 사람이 테스트하기에는 중요도에 비해 시간을 할애하기 어려워진다.
- 테스트 코드가 충분히 있다면 새로운 기능을 개발하거나 수정할때 기존에 있던 기능들은 테스트코드에 의해서 동작이 확인이 되지만 테스트 코드가 없다면 어느 범위까지 사이드이펙트가 발생할지 찾고 테스트를 다시 수행해야하는 불편함이 존재한다.
- 충분한 테스트 코드로 기존 코드가 잘 동작하고 있다는 것을 확인해야한다. → 회귀 테스트

2. 테스트 코드 작성의 원칙
1) FIRST 원칙
1️⃣ F (Fast)
- 테스트 코드는 빠르게 실행되어야한다.
2️⃣ I (Isolated)
- 테스트는 독립적 이어야한다.
- 다른 테스트나 외부 시스템을 의존해서는 안된다.
- 테스트 코드를 통해 검증하고 싶은건 외부 시스템이 정상적으로 동작하는지가 아니라 내 소프트웨어가 정확하게 동작하는지다.
- Isolation 원칙이 깨졌을 때 다음에 소개할 Repeatable 원칙도 함께 위배될 확률이 매우높다.
3️⃣ R (Repeatable)
- 테스트 코드는 반복실행해도 항상 동일한 결과여야한다.
- 변경한것도 없는데 갑자기 테스트 코드가 실패한다면 Repeatable 원칙이 위배된 것이다.
4️⃣ S (Self-validating)
- 테스트 코드는 그 자체로 스스로 검증되어야한다.
- 콘솔 출력문(print()) 혹은 로그 등을 통해 개발자가 직접 검증해야하는 부분이 있으면 안된다.
- 테스트 코드를 CI/CD 배포 파이프라인의 구성요소로 추가하기도 하는데, 이런 상황에서 Self-validating 원칙이 깨진다면 충분히 검증이 안되는 상황이 생길 수 있다.
5️⃣ T (Timely)
- 테스트 코드는 즉시 작성되어야한다.
2) 테스팅 7원칙
1️⃣ 테스트는 결함의 존재를 찾는 행위이다.
- 테스트의 목적은 코드가 완벽함을 증명하는 것이 아니라 다양한 케이스를 통해 버그를 찾기 위함이다.
2️⃣ 완벽한 테스트는 불가능하다.
- 테스트를 수십만, 수백만 번을 진행하고 확인해도 모든 부분을 전부 확인할 수 없다. 안정적인 소프트웨어를 만들기 위해 검증하는 방법중 한 개일 뿐이다. 그렇기 때문에 가장 중요한 부분 먼저 진행함으로써 효과를 극대화 해야한다.
3️⃣ 테스트 구성은 가능한 빠르게 시작한다.
- 개발 초기에 환경을 구축하고 테스트를 진행하여 차후 개발 과정에서의 노력을 최소화한다.
4️⃣ 결함은 집중된다.
- 파레토 법칙: 제품의 결점 중 80%는 20%의 요인으로부터 발생한다는 개념(적은 비율의 원인이 아주 큰 영향을 가져온다)
- 대부분의 결함은 특정 모듈에 의해 발생되는 경우가 많다.
- 복잡한 구조를 가지고 있는 모듈
- 기존것이 아닌 새롭게 개발한 모듈
- 다른 모듈들과 복잡한 상호작용을 하고 있는 모듈
- 대부분의 결함은 특정 모듈에 의해 발생되는 경우가 많다.
5️⃣ 살충제 역설.
- 동일한 테스트가 반복되면 새로운 결함을 발견할 수 없다. 코드가 테스트코드에 면역이 되는 것을 의미한다.
- 기능은 계속 수정되고 발전하고 있는데 테스트 코드가 변함없이 유지된다면 기존에 있던 문제는 계속 확인이 가능하겠지만 새롭게 생긴 문제에 대해서는 확인이 불가능하기 때문에 지속적으로 테스트 코드 리뷰 및 업데이트로 새로 생긴 결함을 검출 해야한다.
6️⃣ 테스트는 정황에 의존적이다.
- 서비스의 특성을 파악하고 그에 맞는 테스트를 진행해야한다. 쇼핑몰을 만든다고 하면 가장 중요한것은 상품을 보여주고 선택하는 것이 아닌 결제이기 때문에 결제 모듈을 먼저 꼼꼼히 테스트 해야한다. 이처럼 각 서비스에서의 중요한 정황이 무엇인지 파악하고 그에 맞는 테스트 코드를 작성해야한다.
7️⃣ 오류가 부재함은 궤변이다
- 테스트 케이스를 꼼꼼히 세우지 않아 발견하지 못한 버그가 발생할 수 있기 때문에 테스트가 모두 통과했다고 서비스에는 문제가 없다고 생각하는 것을 주의하여야 한다. 테스트의 목적은 많은 문제(결함)를 찾는 것에 있다.
3. 테스트의 종류
1) 단위 테스트 (Unit Test)
- 범위: 함수, 메서드, 클래스 등 코드의 최소 단위에 대한 테스트
- 목적: 각 단위(로직)가 올바르게 동작하는지 확인.
- 장점:
- 테스트가 매우 빠르게 실행된다.
- 에러 발생 시 문제 지점을 찾기가 수월하다.
- 단점:
- 실제 환경(DB, 네트워크 등)과는 다른 가짜 객체(Mock/Stub)를 사용하는 경우가 많아, 모듈 간 통합 이슈나 성능 문제 등을 놓칠 수 있다.
- 사용자 시나리오 전반을 확인하기에는 한계가 있다.
2) 통합 테스트 (Integration Test)
- 범위: 여러 단위가 결합된 상태를 테스트하며, DB, 파일 시스템, 외부 API 등 일부 실제 의존성도 포함한다.
- 목적: 모듈 간 상호작용, 데이터 흐름 등을 검증.
- 장점:
- 단위 테스트만으로는 발견하기 어려운 연동 문제나 의존성 이슈를 조기에 파악할 수 있다.
- 실제 환경을 사용함으로써 시스템 전체가 어떻게 동작하는지 확인이 가능하다.
- 단점:
- 단위 테스트보다 실행 속도가 느리고, 세팅 과정이 복잡하다.(DB 연결, 외부 API 호출 등)
- 문제가 발생했을 때, 정확한 원인을 찾기가 비교적 어렵다.
4. 테스트 코드는 어떻게 작성해야할까?
1️⃣ 한 가지 기능에 대해서 여러가지 상황을 생각하며 케이스를 구성
테스트 케이스 구성
- 정상 케이스
- 입력값의 범위가 0~128 일때 10, 50, 100 등을 입력 받았을때 테스트
- 엣지 케이스(경계 조건)
- 입력값의 범위가 10미만일 때 A동작, 10이상 일때 B동작을 해야한다면 9, 10, 11 에 대한 테스트
- 예외 상황
- 입력값이 숫자만 받아야할때 문자를 입력한 경우 예외처리가 되는지 테스트
2️⃣ 각 테스트 및 케이스들은 반드시 독립적으로 수행되어야 한다.
- A, B라는 테스트가 존재할때 B 테스트를 수행하려면 A 테스트가 먼저 수행이되야한다면 FIRST의 I (Isolated) 원칙에 위배되는 행위이다. 반드시 각 테스트들은 독립적으로 수행이 되어야한다.
- 데이터 베이스를 연결해 테스트를 수행할때에도 테스트가 끝나면 데이터들은 반드시 롤백이 되어 테스트 이전상태로 되돌아가야한다.
3️⃣ 의미없는 테스트 코드 작성을 지양하자!
- 테스트 코드를 작성하기 전에 서비스가 사용자에게 제공해야하는 핵심가치가 무엇인지 생각해보고 기능의 중요도를 파악한 후 핵심 로직부터 테스트코드를 작성하는 습관을 들여야한다. 단순히 테스트코드를 많이, 커버리지 수치에만 신경을 쓴다면 정말 중요한 테스트를 놓치고 지나갈 수 있다.
모든 케이스를 커버할 순 없지만, 최대한 커버해야 비교적 안전하고 유지보수가 가능한 소프트웨어 제작이 가능하다.