Why-Integration-Testing
1 통합 테스트를 하는 이유
- 단위 테스트가 비즈니스 로직을 확인하는 데 좋지만 비즈니스 로직을 외부와 단절된 상태로 확인하는 것으로 충분하지 않다
- 각 부분이 데이터베이스나 메시지 버스 등의 외부 시스템과 어떻게 통합되는지 확인해야 한다
2 통합 테스트는 무엇인가?
- 통합 테스트가 무엇인지 알기 위해선 먼저 단위 테스트가 무엇인지 알아야한다
2.1 단위 테스트란?
- 단위 테스트는 다음 세 가지 요구 사항을 충족하는 테스트다
- 단일 동작 단위를 검증하고
- 빠르게 수행하고
- 다른 테스트와 별도로 처리한다
2.2 통합 테스트란?
- 단위 테스트의 3 가지 요구 사항 중 하나라도 충족하지 못하는 테스트는 통합 테스트 범주에 속한다
- 따라서 단위 테스트가 아닌 모든 테스트가 통합 테스트에 해당된다
- 실제로 통합 테스트는 시스템이 프로세스 외부 의존성과 통합해 어떻게 동작하는지 검증한다
단위 테스트와 통합 테스트 비교
- 단위 테스트는 도메인 모델을 다루는 반면, 통합 테스트는 프로세스 외부 의존성과 도메인 모 델을 연결하는 코드를 확인한다
2.3 테스트 피라미드
- 단위 테스트와 통합 테스트 간의 균형을 유지하는 것이 중요하다
- 균형을 유지한다는 것은 단위 테스트의 개수와 통합 테스트 개수의 균형을 맞추는 것
2.3.1 통합 테스트의 장단점
통합 테스트의 장점
- 코드를 더 많이 실행시켜 회귀 방지가 단위 테스트보다 우수하다
- 애플리케이션 코드, 라이브러리 코드를 모두 포함하기 때문
- 제품 코드와의 결합도가 낮아 리팩터링 내성도 우수하다
통합 테스트의 단점
- 통합 테스트가 외부 의존성에 직접 작동하며 느려지며, 이러한 테스트는 유지비가 많이 든다
- 유지비 증가의 이유
- 프로세스 외부 의존성 운영이 필요
- 관련된 협력자가 많아 테스트가 비대해짐
2.3.2 단위 테스트의 장단점
단위 테스트의 장 점
- 빠른 피드백이 가능하며 유지비가 적게 든다
단위 테스트의 단점
- 통합 테스트와 비교해 회귀 방지와 리팩터링 내성이 떨어진다
2.3.3 테스트 비율
- 단위 테스트와 통합 테스트의 비율은 프로젝트의 특성에 따라 다를 수 있지만 일반적으로 아래와 같다
- 단위 테스트로 가능한 많은 비즈니스 시나리오의 예외 상황을 확인한다
- 통합 테스트는 주요 흐름과 단위 테스트가 다루지 못하는 기타 예외 상황을 다룬다
- 주요 흐름은 시나리오의 성공적인 실행을 의미한다
- 예외 사항은 비즈니스 시나리오 수행 중 오류가 발생하는 경우다
- 대부분을 단위 테스트로 전환하면 유지비를 절감할 수 있다
- 중요한 통합 테스트가 비즈니스 시나리오당 하나 또는 두 개 있으면 시스템 전체의 정확도를 보장할 수 있다
- 이 지침을 따르면 테스트 피라미드의 형태가 나타날 것이다
- Good-Unit-Test.md - 테스트 피라미드 참고
2.2 통합 테스트와 빠른 실패
- 통합 테스트에서 프로세스 외부 의존성과의 상호 작용을 모두 확인하려면 가장 긴 주요 흐름을 선택해야한다
- 가장 긴 주요 흐름은 모든 프로세스 외부 의존성을 거치는 것이다
- 이렇게 모든 상호 작용을 거치는 흐름이 없으면, 외부 시스템과의 통신을 모두 확인하는 데 필요한 만큼 통합 테스트를 추가로 작성하라
3 어떤 프로세스 외부 의존성을 직접 테스트하나?
- 통합 테스트는 시스템이 프로세스 외부 의존성과 어떻게 통합하는지를 검증한다
- 이러한 검증을 구현하는 방식은 두 가지가 있다
- 실제 프로세스 외부 의존성을 사용하기
- 목으로 대체하기
- 두 가지 방식을 각각 언제 적용해야 될까?
3.1 프로세스 외부 의존성의 두 가지 유형
- 모든 프로세스 외부 의존성은 두 가지 범주로 나뉜다
- 관리 의존성
- 비관리 의존성
- 관리 의존성은 실제 인스턴스를 사용하고 비관리 의존성은 목으로 대체하라
3.1.1 관리 의존성
- 전체를 제어할 수 있는 프로세스 외부 의존성을 관리 의존성이라 한다
- 이러한 의존성은 애플리케이션을 통해서만 접근할 수 있다
- 따라서 해당 의존성과의 상호 작용은 외부 환경에서 볼 수 없다
- 좋은 예로 애플리케이션 데이터베이스가 있는데 이는 애플리케이션만 사용하는 데이터베이스르 말한다
- 따라서 외부 시스템은 데이터베이스에 직접 접근하지 않고 애플리케이션에서 제공하는 API를 통해 접근한다
- 관리 의존성과의 통신은 구현 세부사항이다
- 관리 의존성과 통신하는 것은 애플리케이션뿐이므로 하위 호환성을 유지할 필요가 없다
- 하위 호환성을 유지할 필요가 없다는 말은 외부 클라이언트는 데이터베이스를 어떻게 구성하는지 신경 쓰지 않는다는 것을 의미한다
- 따라서 기존 기능을 손상시키지 않고 애플리케이션과 애플리케이션 데이터베이스 간의 통신 패턴을 원하는 대로 수정할 수 있다
- 애플리케이션의 클라이언트의 시야에서 완전히 숨어있기 때문에 이와 같은 일이 가능하다
- 관리 의존성은 통합 테스트에서 해당 의존성을 그대로 사용하라
- Mocks-And-Test-Fragility.md 참조
3.1.2 비관리 의존성
- 전체를 제어할 수 없는 프로세스 외부 의존성
- 해당 의존성과의 상호 작용을 외부에서 볼 수 있다
- 예를 들어 SMTP 서버와 메시지 버스 등이 있다
- 둘 다 다른 애플리케이션에서 볼 수 있는 부작용을 발생시킴
- 비관리 의존성과의 통신은 시스템의 식별할 수 있는 동작이다
- 해당 의존성은 목으로 대체해야 한다
3.1.3 통합 테스트에서 실제 데이터베이스를 사용할 수 없는 경우
- 통합 테스트에서 관리 의존성을 실제 버전으로 사용할 수 없는 경우도 있다
- 관리 의존성임에도 불구하고 목으로 처리해야할까?
- 관리 의존성을 목으로 대체하면
- 리팩터링 내성이 저하된다
- 회귀 방지도 떨어진다
- 데이터베이스가 프로젝트의 유일한 프로세스 외부 의존성이면 회귀 방지에 있어 단위 테스트와 다를 바 없다
- 회귀 방지가 떨어진다는 것은 실행 되는 코드의 양이 준다는 것과 같다 데이터베이스를 목으로 대체하면 컨트롤러에 코드 몇줄만 실행될 것
- 따라서 통합 테스트에서 실제 데이터베이스를 사용할 수 없는 경우 아예 테스트를 작성하지 말고 도메인 모델의 단위 테스트에 집중하라
- 가치가 충분하지 않은 테스트는 테스트 스위트에 있어서는 안 된다
4 의존성 추상화를 위한 인터페이스 사용
- 단위 테스트 영역에서 가장 많이 오해하는 주제 중 하나는 인터페이스 사용이다
- 개발자들은 인테페이스를 둔 이유를 자주 잘못 설명하고 인터페이스를 남용하는 경향이 있다
4.1 인터페이스와 느슨한 결합
- 많은 개발자가 데이터베이스나 메시지 버스와 같은 프로세스 외부 의존성을 위해 인터페이스를 도입한다
- 심지어 인터페이스의 구현이 하나만 있는 경우도 있다
구현이 하나인 인터페이스를 사용하는 이유
- 구현이 하나인 인터페이스를 사용하는 일반적 이유는 인터페이스가
- 프로세스 외부 의존성을 추상화해 느슨한 결합을 달성하고
- 기존 코드를 변경하지 않고 새로운 기능을 추가해 공개 폐쇄 원칙을 지키키 때문이다
- 이 두가지 이유 모두 오해다
구현이 하나인 인터페이스를 사용하면 안되는 이유