-
Notifications
You must be signed in to change notification settings - Fork 0
Description
5.2 기능 테스트 자동화
5.2.1 기능 테스트 자동화에서의 테스트 전략
아키텍트가 기능 테스트를 다루는 이유는 기능 테스트의 ‘내용’을 검증하기보다 테스트 자동화 체계의 설계와 구현, 즉 테스트 툴선정, 테스트 코드 구조 설계, 유지보수 전략 수립 등의 과정에서 아키텍트의 기술적 지식과 경험이 필수적이기 때문입니다.
기능 테스트 자동화 전략으로는 테스트 피라미드Test Pyramid가 널리 알려져 있습니다.
이는 단위 테스트, 통합 테스트, E2E 테스트 순으로 구성되며, 이때 테스트 양을 점차 줄여간다는 원칙을 시각화한 모델입니다.
단위 테스트만으로는 컴포넌트 통합 후 전체가 제대로 작동한다는 것을 보장할 수 없으므로 통합 테스트와 E2E 테스트도 병행해야 합니다.
하지만 이 피라미드는 참조 모델일 뿐, 각 층의 면적 비율이 실제 테스트의 양을 그대로 나타내는 것은 아닙니다.
테스트 양에 대해 정해진 ‘황금비율’은 없습니다.
이러한 점을 고려해 테스트 피라미드를 기본 가이드로 삼되, 프로젝트 특성에 맞춰 각 자동화 테스트의 유형별 정책과 기준을 수립하는 것이 테스트 전략의 핵심 입니다.
5.2.2 단위 테스트
단위 테스트unit testing는 소프트웨어 구성 요소의 최소 단위가 올바르게 동작하는지 검증하는 테스트입니다.
프로그램의 최소 단위로 보기
객체 지향 언어에서는 클래스가 이에 해당하며, 클래스마다 하나의 전용 테스트 클래스를 생성합니다.
만약 의존성이 있다면, 실제 객체 대신 테스트 더블test double을 사용하여 해당 동작을 모방합니다.
COLUMN 테스트 더블
테스트 더블에는 다양한 유형이 있습니다.
여기서 SUTsystem under test는 테스트 대상이 되는 단위를, DOCdepended-on component는 SUT가 의존하는 구성 요소를 의미합니다.
표 5.2 테스트 더블의 다양한 유형
| 분류 | 특징 |
|---|---|
| 스텁(Test Stub) | 실제 DOC 대신 사전에 정의된 응답을 반환. 호출된 메서드에 대해 정해진 결과만 제공한다. |
| 스파이(Test Spy) | 스텁 기능에 더해, 메서드 호출 내역(호출 횟수, 인자 값 등)을 기록하여 이후 검증에 활용한다. |
| 목(Mock Object) | SUT와 DOC 간 상호작용에 대한 기대 조건(호출 횟수, 인자 조건 등)을 미리 정의해 두고, 테스트 중 실제 동작이 그 기대에 부합하는지 검증한다. |
| 페이크(Fake Object) | 실제 DOC와 유사한 동작을 수행하지만, 더 단순하고 가벼운 방식으로 구현된 테스트용 구성 요소이다. (실제 데이터베이스 대신 메모리를 사용하는 임시 저장소 등) |
| 더미(Dummy Object) | 기능은 없으며, 단지 메서드 인자 전달을 위해서만 사용된다. (엄밀히 말하면, 테스트 더블에 포함되지 않음) |
동작 단위로 보기
두 번째 방식은 컴포넌트를 단위 테스트의 최소 단위로 간주하는 관점입니다.
『Beyond Legacy Code』(Pragmatic Bookshelf, 2015 )에서는 단위 테스트의 ‘단위’를 다음과 같이 정의합니다.
단위란 동작의 단위, 다시 말해 독립적이고 검증 가능한 동작을 의미한다. 이것은 명확한 차별점을 가져야 하며, 시스템의 다른 동작과 밀접하게 연관되어서는 안 된다.
이 정의는 이 책에서 설명한 컴포넌트의 개념, 즉 ‘특정 동작을 제공할 책임이 있고, 명확한 인터페이스로 정의된 소프트웨어 구성 요소’와 일치합니다.
컴포넌트를 적절한 단위로 분할하는 일은 좋은 설계의 출발점이 되며, 테스트 코드 작성과 리팩터링을 반복하면서 최적의 ‘동작 단위’를 찾아가는 과정 자체가 중요합니다.
단위 테스트의 특징
- 테스트 입력값과 의존 객체 준비가 용이함
- 다양한 테스트 케이스를 간편하게 구성 가능
- 테스트 실행 시간이 짧음
- 테스트 실패 시 원인 파악이 빠름
5.2.3 통합 테스트
통합 테스트integration testing는 여러 단위나 컴포넌트를 결합했을 때, 이들이 집합체로서 정상적으로 동작하는지 검증하는 과정으로, 단위 테스트와 E2E 테스트 사이의 범주 전체를 포괄합니다.
통합 테스트의 주요 특징은 다음과 같습니다.
- 컴포넌트 간 상호작용 검증이 가능함
- 유스케이스 또는 그 일부 단계 등 주요 흐름 테스트에 적합함
- 테스트 준비 비용이 큼(컴포넌트 생성·설정, 테스트 데이터 준비 등)
- 세밀한 테스트 케이스 검증에는 적합하지 않음
- 테스트 실행 시간이 길어지는 경향 있음(특히 데이터베이스 및 파일 등 I/O 작업 포함 시)
- 테스트 실패 시 원인 파악이 어려움
5.2.4 E2E 테스트
‘End-to-End’는 시스템 구성의 ‘끝에서 끝까지’ 전체 흐름을 확인 한다는 의미로, 사용자 인터페이스부터 데이터베이스, 외부 연동 서비스에 이르 기까지 시스템의 모든 계층과 구성 요소를 연결해 검증합니다.
E2E 테스트 툴
웹 애플리케이션에서는 사용자의 브라우저 동작을 시뮬레이션하여 테스트 시나리오를 자동 실행할 수 있는 E2E 테스트 툴이 널리 활용됩니다.
이러한 툴은 크게 다음 두 가지 방식으로 나뉩니다.
- 코드로 테스트 스크립트를 작성하는 방식
- 레코드 앤드 리플레이Record and Replay(RnR) 방식으로 스크립트를 생성하는 노코드 방식
개발자가 테스트를 직접 작성할 경우에는 코드형 툴이 세밀한 제어에 유리하고, QA 엔지니어가 작성할 경우에는 노코드형 툴을 도입하는 것이 효율적일 수 있습니다.
E2E 테스트의 특징
- 시스템 전체를 검증할 수 있음
- 테스트 준비(애플리케이션 빌드 및 환경 배포 등)에 시간이 많이 소요됨
- 사용자 이용을 위한 사전 준비(각종 설정, 마스터 데이터 준비 등)에 많은 리소스 필요
- 세밀한 테스트 케이스 검증에는 적합하지 않음
- 테스트 실행 시간이 매우 김
- 테스트 실패 시 원인 파악이 어려움
COLUMN 행동 주도 개발(BDD)
통합 테스트나 E2E 테스트에서도 테스트 코드를 먼저 작성하는 접근 방식도 있는데, 바로 행동 주도 개발behavior-driven development (BDD)입니다.
BDD에서는 유저 스토리로 정의된 기능을 구현하기 전에, 해당 기능의 동작 (행동)을 요구 조건의 형태로 명확하게 정의합니다.
도메인 전문가, 개발자, QA 엔지니어가 함께 대화를 통해 사양을 구체화해 나갑니다.
대화를 통해 명확해진 사양은 요구 조건으로 정리되어 자연어로 기술됩니다.
대화 중에 정리된 도메인 지식을 유비쿼터스 언어로 통일하여 기술합니다.
유비쿼터스 언어는 도메인 주도 설계(DDD)의 핵심 개념 중하나로, 관계자들이 공유하는 공통된 어휘를 뜻합니다.
요구 조건은 구체적인 예시를 바탕으로 명확히 정의되기 때문에 그대로 테스트 케이스로 활용할 수 있습니다.
5.2.5 테스트 전략 검토 시 고려사항
단위 테스트 핵심 원칙
커버리지는 높을수록 좋기 때문에 100%를 이상적인 상태로 여길 수도 있습니다.
그러나 이 수준에 도달하려면 상당한 비용이 들고, 이를 계속 유지하는 것도 쉬운 일이 아닙니다.
이것이 부담으로 작용해 개발 속도가 느려질 위험도 존재합니다.
구현 세부 사항을 검증하는 테스트는 리팩터링 등 변경에 취약해 자주 실패할 수 있어 피하는 것이 좋고, 검증의 초점은 구현이 아니라 어디까지나 관찰 가능한 동작에 맞춰야 합니다.
프로세스 로직의 정확성은 단위 테스트에서 검증하기보다는 상위 테스트(통합 테스트나 E2E 테스트)에 맡기는 것이 좋다는 것이 필자의 의견입니다.
프레젠테이션 계층의 컨트롤러나 도메인 계층의 애플리케이션 서비스 등 프로세스 로직에 해당하는 컴포넌트는 단위 테스트 대상에서 제외하고, 핵심 로직에 해당하는 컴포넌트에 대해 집중적인 단위 테스트를 수행하는 것입니다.
커버리지는 높을수록 좋지만 100%라는 높은 수치를 절대적인 목표로 삼으면 본래의 단위 테스트의 취지에서 벗어나 커버리지를 높이기 위한 테스트 코드를 작성하게 될 수 있습니다.
프로젝트마다 적절한 기준을 설정하는 것이 좋습니다. 필자의 경험상, 테스트 주도 개발이나 테스트 퍼스트로 개발을 진행하면 자연스럽게 90% 이상의 커버리지에 도달하게 되므로 90%는 실질적인 기준으로 삼기에 충분히 타당한 수치입니다.
통합 테스트 핵심 원칙
비즈니스 규칙이나 도메인 로직의 복잡성을 해결하기 위해 클린 아키텍처를 채택했다면 유스케이스 계층과 엔티티 계층에 위치한 컴포넌트를 통합 범위로 설정하는 것이 기본적인 접근입니다.
통합 테스트는 구성과 유지 관리에 상대적으로 비용이 많이 듭니다.
만약 이 비용이 기대 효과에 비해 과도하다고 판단된다면, 통합 테스트를 최소화하고 E2E 테스트로 커버하는 전략도 검토할 수 있습니다.
E2E 테스트 핵심 원칙
E2E 테스트는 기본적으로 리그레션 테스트의 역할에만 집중합니다 (단, BDD를 적용하는 경우는 예외일 수 있습니다).
따라서 실제 개발 과정에서는 기능을 먼저 구현한 뒤, 테스트 담당자가 직접 동작을 검증하고 나서 E2E 테스트를 작성하는 순서로 진행하는 것이 효과적입니다.
E2E 테스트의 주된 목적은 시스템 기능이 변경이나 오류로 인해 제대로 동작하지 않게 된 경우 이를 감지하는 데 있습니다.
이러한 관점에서 E2E 테스트는 다음과 같은 항목들을 검토 합니다.
- 주요 성공 시나리오의 실행 가능 여부
- 대표적 예외 시나리오 및 대체 시나리오의 실행 가능 여부
COLUMN 테스트 코드에 대한 투자
테스트 코드는 프로덕션 코드와 마찬가지로 소프트웨어의 중요한 자산으로 여겨야 합니다.
내부 품질을 높이고 유지하는 데 필수적인 요소이기 때문입니다.
테스트 코드는 그 구조가 쉽게 비대해 지고, 이를 방치하면 정돈되지 않은 상태로 흐트러지기 마련입니다.
제대로 관리되지 않은 테스트 코드는 다음과 같은 문제를 초래하기도 합니다.
- 특정 테스트 케이스의 위치를 파악하기 어려움
- 테스트 범위의 누락 여부를 확인하기 어려움
- 테스트 코드만으로 테스트의 목적이나 의도를 파악하기 어려움
코드의 가독성과 유지보수성을 높이기 위해 기억해야 할 세 가지 원칙이 있습니다.
필자는 각 머릿글자를 따서 이를 ‘테스트 코드의 SOS’라 부릅니다.
- Structured (구조화)
- Organized (체계적 정리)
- Self-documenting (자체 문서화)
구조화란 테스트 관점에 따라 테스트 케이스를 분류하고, 그 기준이 코드 구조에 반영된 상태를 의미합니다.
체계적 정리란 테스트 케이스의 커버리지와 적절성을 쉽게 확인하고 검증할 수있는 상태를 의미합니다.
자체 문서화란 별도의 문서나 설명없이, 테스트 코드만으로 테스트의 목적과 조건을 명확히 파악할 수 있는 상태를 말합니다.
Metadata
Metadata
Assignees
Labels
Projects
Status
