통합 테스트와 E2E 테스트 소개
지난 장에서는 여러분이 작성하는 코드의 가장 작은 단위인 함수나 모듈을 개별적으로 검증하는 단위 테스트(Unit Testing) 의 중요성과 Jest를 활용한 기본적인 작성 방법을 배웠습니다. 단위 테스트는 개별 컴포넌트의 신뢰성을 높이고 버그를 빠르게 찾아내는 데 매우 효과적입니다.
하지만 애플리케이션은 단순히 개별 단위들의 합이 아닙니다. 여러 단위들이 서로 상호작용하고 데이터를 주고받으며, 하나의 큰 기능을 완성합니다. 이때, 각 단위가 개별적으로는 문제가 없더라도, 서로 연결되었을 때 예상치 못한 문제가 발생할 수 있습니다. 예를 들어, 로그인 컴포넌트
와 사용자 정보 표시 컴포넌트
가 각각 단위 테스트를 통과했더라도, 로그인 성공 후 사용자 정보가 제대로 전달되어 표시되는지 여부는 단위 테스트만으로는 보장하기 어렵습니다.
이러한 문제를 해결하고 애플리케이션의 더 큰 흐름을 검증하기 위해 통합 테스트(Integration Testing) 와 E2E(End-to-End) 테스트가 필요합니다. 이번 장에서는 이 두 가지 테스트의 개념과 목적, 그리고 각각 어떤 시나리오에서 활용되는지를 소개하고, 주요 도구들을 간략히 알아보겠습니다.
테스트 피라미드: 다양한 테스트 레벨의 조화
소프트웨어 테스팅에는 여러 레벨이 있으며, 이들은 일반적으로 테스트 피라미드(Test Pyramid) 라는 개념으로 설명됩니다.
E2E (UI) Test (적음, 느림, 비쌈)
/|\
/ | \
/ | \
통합 (Integration) Test (중간, 보통, 적당)
/ /|\ \ \
/ / | \ \ \
/ / | \ \ \
단위 (Unit) Test (많음, 빠름, 저렴)
- 단위 테스트 (Unit Test): 가장 하단에 위치하며, 개수가 가장 많고, 실행 속도가 빠르며, 비용이 저렴합니다. 코드의 최소 단위(함수, 메서드)를 격리하여 테스트합니다.
- 통합 테스트 (Integration Test): 중간에 위치하며, 여러 단위들이 함께 작동하는 방식(모듈 간의 상호작용, 데이터 흐름 등)을 테스트합니다. 단위 테스트보다는 개수가 적고 느리지만, 시스템의 연결성을 검증하는 데 필수적입니다.
- E2E 테스트 (End-to-End Test): 최상단에 위치하며, 개수가 가장 적고, 실행 속도가 가장 느리며, 비용이 가장 비쌉니다. 실제 사용자의 관점에서 애플리케이션의 전체 흐름(UI, 백엔드, DB 등 모든 시스템)을 시뮬레이션하여 테스트합니다.
테스트 피라미드는 하단으로 갈수록 테스트의 개수가 많아지고, 상단으로 갈수록 개수가 적어지는 형태를 가집니다. 이는 빠른 피드백과 효율성을 위해 대부분의 테스트를 단위 테스트로 작성하고, 점점 더 넓은 범위를 커버하는 통합 및 E2E 테스트는 꼭 필요한 부분에만 적용해야 함을 의미합니다.
통합 테스트 (Integration Testing)
통합 테스트는 두 개 이상의 모듈 또는 컴포넌트가 함께 작동할 때 발생하는 상호작용과 데이터 흐름을 검증하는 테스트입니다. 즉, 단위 테스트에서 검증한 개별 기능들이 서로 연결되었을 때도 올바르게 동작하는지 확인합니다.
목적 및 필요성
- 모듈 간의 인터페이스 검증: 서로 다른 모듈이 데이터를 주고받는 방식이 올바른지 확인합니다.
- 데이터 흐름 검증: 한 모듈의 출력이 다른 모듈의 입력으로 사용될 때, 데이터가 올바르게 전달되고 처리되는지 확인합니다.
- 서브시스템 결합 확인: 데이터베이스, 외부 API 등의 의존성과 연동이 잘 되는지 부분적으로 검증합니다. (단, 실제 외부 시스템이 아닌 '모의(Mock)' 객체를 사용하여 테스트하기도 함)
단위 테스트와의 차이점
- 단위 테스트: 각 컴포넌트를 격리하여 테스트합니다. 다른 컴포넌트나 외부 의존성은 모의(Mock) 객체나 스텁(Stub)으로 대체하여 테스트 환경을 통제합니다.
- 통합 테스트: 실제 컴포넌트들이 서로 연결된 상태에서 테스트합니다. 의존성도 실제 의존성을 사용하거나, 최소한의 모의 객체로 대체하여 실제 환경에 가깝게 테스트합니다.
통합 테스트 시나리오 예시
- 사용자 로그인 기능:
로그인 폼 컴포넌트
와인증 로직 모듈
,API 호출 서비스
가 함께 작동하여 사용자가 성공적으로 로그인하고 인증 토큰을 받는지 확인. (여기서 API 호출은 실제 서버가 아닌 '모의 서버'나 'Mock API'를 사용할 수 있음) - 장바구니 추가:
상품 선택 컴포넌트
와장바구니 상태 관리 모듈
이 연동되어 상품이 장바구니에 올바르게 추가되고 총 가격이 업데이트되는지 확인. - 데이터베이스 연동:
데이터 저장 함수
와데이터베이스 드라이버
가 연동되어 특정 데이터가 데이터베이스에 올바르게 저장되는지 확인. (실제 개발용 데이터베이스를 사용할 수 있음)
통합 테스트 도구 (간략)
통합 테스트는 특정 도구에 얽매이기보다는 어떤 범위까지 테스트할 것인지에 따라 Jest와 같은 단위 테스트 프레임워크를 확장해서 사용하거나, 더 나아가 Cypress, Playwright 같은 E2E 테스트 도구의 일부 기능을 활용하기도 합니다.
- Jest (확장):
jest.mock()
을 사용하여 외부 의존성을 부분적으로 모의 처리하고, 여러 모듈을 함께 임포트하여 테스트합니다. - Testing Library: 특정 프레임워크(React, Vue 등)의 컴포넌트 통합 테스트에 유용하며, 사용자 관점에서 DOM에 접근하여 테스트합니다.
- MSW (Mock Service Worker): 네트워크 요청을 가로채서 모의 응답을 제공하여, 실제 백엔드 없이도 프론트엔드의 네트워크 관련 통합 테스트를 수행할 수 있게 합니다.
E2E 테스트 (End-to-End Testing)
E2E (End-to-End) 테스트는 애플리케이션의 시작부터 끝까지, 즉 실제 사용자가 웹 애플리케이션을 사용하는 것과 동일한 방식으로 모든 시스템(프론트엔드 UI, 백엔드 서버, 데이터베이스, 네트워크 등)이 통합되어 올바르게 작동하는지 검증하는 테스트입니다. 이는 가장 높은 수준의 테스트이며, 사용자 경험 전체를 포괄합니다.
목적 및 필요성
- 사용자 시나리오 검증: 핵심 비즈니스 흐름이 실제 환경에서 문제없이 작동하는지 확인합니다. (예: "회원가입 -> 로그인 -> 상품 검색 -> 장바구니 추가 -> 결제 완료"와 같은 전체 흐름)
- 시스템 통합 검증: 프론트엔드와 백엔드, 데이터베이스 등 모든 시스템이 완전히 연동되어 예상대로 동작하는지 확인합니다.
- 배포 전 최종 검증: 실제 사용자에게 서비스를 제공하기 전에 마지막으로 애플리케이션의 안정성을 확인합니다.
단점
- 느린 실행 속도: 실제 브라우저를 띄우고, 네트워크 요청을 보내고, UI를 렌더링하는 등 모든 과정을 시뮬레이션하므로 실행 시간이 가장 오래 걸립니다.
- 높은 비용: 테스트 작성, 유지보수, 실행 환경 설정 등에 많은 시간과 노력이 필요합니다.
- 불안정성: 네트워크 지연, 외부 서비스의 일시적인 문제, UI 변경 등으로 인해 '실패하지 않아야 할 테스트'가 실패하는 '플래키(flaky) 테스트'가 발생하기 쉽습니다.
E2E 테스트 시나리오 예시
- 회원 가입 및 로그인 흐름: 사용자가 회원가입 폼을 채우고 제출하여 계정을 생성한 후, 해당 계정으로 로그인하여 대시보드에 접근하는 전 과정을 테스트합니다.
- 상품 구매 프로세스: 사용자가 상품을 검색하고, 장바구니에 추가하며, 결제 정보를 입력하고, 주문을 완료하는 전체 흐름을 테스트합니다.
- 관리자 페이지 기능: 관리자가 로그인하여 사용자 목록을 조회하고, 특정 사용자의 권한을 변경한 후, 변경된 권한이 적용되었는지 확인하는 흐름을 테스트합니다.
E2E 테스트 도구 (간략)
E2E 테스트는 주로 실제 브라우저를 자동화하여 사용자 상호작용을 시뮬레이션하는 도구들을 사용합니다.
- Cypress: 프론트엔드 중심의 E2E 테스트 도구로, 빠른 실행과 디버깅 용이성이 장점입니다. 테스트 환경을 직접 제어할 수 있어 안정적입니다.
- Playwright: Microsoft에서 개발한 도구로, Chromium, Firefox, WebKit 등 다양한 브라우저를 지원하며, Node.js, Python 등 여러 언어로 테스트를 작성할 수 있습니다.
- Selenium: 가장 오래되고 널리 사용되는 웹 자동화 도구입니다. 다양한 언어 바인딩과 브라우저 지원이 강점이지만, 설정이 복잡할 수 있습니다.
마무리하며
이번 장에서는 단위 테스트를 넘어 애플리케이션의 더 큰 그림을 검증하는 통합 테스트(Integration Testing) 와 E2E 테스트(End-to-End Testing) 에 대해 학습했습니다.
여러분은 테스트 피라미드 개념을 통해 각 테스트 레벨의 역할과 중요성을 이해했습니다. 통합 테스트가 여러 모듈 간의 상호작용과 데이터 흐름을 검증하는 데 중점을 두며, 단위 테스트와 E2E 테스트 사이의 중요한 연결 고리 역할을 한다는 것을 알았습니다. 또한, E2E 테스트가 실제 사용자 관점에서 애플리케이션의 전체 흐름을 시뮬레이션하여 최종적인 사용자 경험을 보장하지만, 느린 속도와 높은 비용이라는 단점도 있다는 것을 인지했습니다.
성공적인 소프트웨어 개발을 위해서는 이 세 가지 테스트 레벨을 적절히 조합하여 사용하는 것이 중요합니다. 대부분의 비즈니스 로직은 빠르고 안정적인 단위 테스트로 커버하고, 핵심적인 기능의 통합과 사용자 흐름은 통합 테스트와 E2E 테스트로 검증하는 전략이 필요합니다. 이러한 다층적인 테스팅 접근 방식은 버그를 조기에 발견하고, 코드의 신뢰도를 높이며, 자신감 있는 배포를 가능하게 할 것입니다.