Dev/테스트 코드

더 나은 테스트 전략 수립(테스트 수준/ 테스트 레시피/ 배포 파이프라인)

싯벨트 2025. 3. 28. 17:08
728x90

1. 일반적인 테스트 유형과 수준

테스트의 수준이 높아질수록 신뢰도는 올라가지만 더 복잡해지고, 불안정해지며, 느려집니다. 테스트에는 어떠한 유형이 있고, 이들을 어떤 요소들을 기준으로, 얼마만큼 구현해야 하는지를 살펴보도록 하겠습니다.

일반적인 소프트웨어 테스트 유형 및 수준

1.1. 테스트 평가 기준

어떤 테스트 유형을 사용할지 결정을 내리기 위해서는 먼저 명확한 기준이 필요합니다. 테스트를 평가할 때는 일반적으로 다음 5가지 기준을 사용합니다.

기준 평가 점수 범위 비고 점수 평가
복잡성(complexity) 1-5 작성, 디버깅, 읽기 과정이 얼마나 복잡한가 낮을수록 좋다
불안정성(flakiness) 1-5 제어할 수 없는 요소(다른 팀의 코드, 네트워크, 데이터베이스, 설정 권한)로 테스트가 실패할 가능성이 얼마나 높은가 낮을수록 좋다
신뢰도(confidence) 1-5 통과했을 때 얼마나 신뢰할 수 있는가 높을수록 좋다
유지 보수성(maintainability) 1-5 변경이 얼마나 쉬운가 높을수록 좋다
실행 속도(speed) 1-5 얼마나 빨리 끝나는가 높을수록 좋다

1.1.1. 단위 테스트/ 컴포넌트 테스트 (저수준 테스트)

단위 테스트와 컴포넌트 테스트는 메모리에서 실행되는 저수준 테스트로 외부 의존성 없이, 사용하는 모든 요소에 대한 제어권을 가지고 있습니다. 컴포넌트 테스트는 단위 테스트보다 좀 더 높은 수준의 테스트로, 더 많은 함수와 클래스를 작업 단위 내에 포함할 수 있다는 점에서 차이가 있습니다.

기준 평가 점수 범위 비고
복잡성 1-1 테스트 범위가 작음
불안정성 1-1 모든 것을 제어할 수 있음
신뢰도 1-2 시스템 동작을 확인하기는 어려움
유지 보수성 5-5 유지 보수가 가장 쉬움
실행 속도 5-5 모든 것이 메모리에서 실행되기 때문에 빠름

1.1.2. 통합 테스트

단위 테스트와 같이 메모리 내에서 시스템 객체를 생성하고 그 객체의 특정 기능을 호출하여 테스트를 진행한다는 점은 동일하지만, 실제 환경 설정이나 데이터베이스, 파일 시스템일부 의존성을 그대로 사용하는 경우입니다.

기준 평가 점수 범위 비고
불안정성 2-5 실제 의존성 수에 따라 달라짐
신뢰도 2-5 제어할 수 없는 요소의 정상 동작을 확인할 수 있음
유지 보수성 3-5 외부 의존성이 있기 때문에 단위 테스트 대비 복잡함
실행 속도 3-5 외부 의존성(파일시스템, 데이터베이스, 스레드 등)이 있기 때문에 단위 테스트 대비 느림

1.1.3. API 테스트

애플리케이션의 일부라도 배포를 하고 네트워크로 호출하는 테스트입니다. 즉, 네트워크 서비스의 배포라는 새로운 의존성이 추가됩니다.

기준 평가 점수 범위 비고
불안정성 3-5 네트워크를 사용하면서 불안정성이 높아짐
신뢰도 3-5 해당 API의 정상 동작을 신뢰할 수 있음
유지 보수성 2-5 네트워크는 환경 설정을 더 복잡하게 하고, 테스트 및 API가 변경될 때 더 세심한 작업이 요구됨
실행 속도 2-5 네트워크 때문에 속도가 느려짐

1.1.4. E2E/UI 격리 테스트 - 개발 서버

사용자 관점에서 애플리케이션을 테스트합니다. 테스트 대상인 애플리케이션만 테스트하기 때문에 격리라는 표현을 사용합니다. 타사의 인증 절차나 다른 애플리케이션의 API 등은 모두 가짜 코드로 만들어줍니다.

기준 적정 점수 범위 비고
불안정성 4-5 실제 의존성이 많기 때문에 타임아웃 발생 등 오류 발생 가능성이 높음
신뢰도 4-5 애플리케이션 동작에 대해 신뢰할 수 있음
유지 보수성 1-5 의존성이 많기 때문에 복잡성이 커지고, 테스트가 길어지기 때문에 여러 단계로 나누어 테스트를 진행함
실행 속도 1-5 로그인, 캐싱, 여러 페이지 탐색 등으로 속도가 느림

1.1.5. E2E/UI 격리 테스트 - 스테이징 서버

실제 운영 환경에 가장 가까운 상태에서 수행하며, 가짜인 것이 없는 테스트입니다.

기준 적정 점수 범위 비고
불안정성 5-5 오만 가지 이유로 실패할 수 있음
신뢰도 5-5 관련된 모든 코드를 신뢰할 수 있음
유지 보수성 1-5 의존성이 많고, 작업 흐름이 길어서 유지 보수가 어려움
실행 속도 1-5 실제 의존성을 사용하기 때문에 테스트가 매우 느림.

2. 각 테스트 수준마다 존재하는 안티 패턴

2.1. E2E 테스트만 사용

이 수준의 테스트만 사용한다면 테스트는 매우 느리고, 유지보수도 어렵고, 디버깅도 힘들고, 불안정성이 높습니다. 따라서 비용은 계속 발생하지만, 새로운 E2E 테스트를 작성하는 것에서 얻는 가치는 점점 줄어들게 됩니다.

첫 번째 테스트에서는 높은 신뢰도를 얻을 수 있지만 두 번째 테스트를 작성하면서 얻을 수 있는 신뢰도는 첫 번째 테스트에서 얻은 신뢰도와 크게 차이 나지 않을 것입니다. 더 낮은 수준에서 테스트를 하고 동일한 신뢰도를 얻을 수 있는지 고민해봐야 합니다.

2.2. 저수준 테스트만 사용

단위 테스트는 빠른 결과를 얻을 수 있지만 애플리케이션이 제대로 작동하는지에 대한 신뢰도는 얻을 수 없습니다. 결국 작성된 테스트를 모두 통과했을지라도 수동으로 디버깅과 테스트를 수행해야 합니다.

2.3. 저수준 테스트와 고수준 테스트의 단절

각 수준을 테스트하는 사람 또는 조직이 달라서 서로 다른 파이프라인에서 각기 다른 유형의 테스트를 실행하는 경우입니다. 테스트 중복이 발생할 가능성이 높고, 고수준만 사용하거나 저수준만 사용했을 때 나타나는 단점을 모두 내포할 위험이 높습니다.

3. 테스트 레시피 전략

테스트 레시피특정 기능을 어떻게 테스트할지 간단한 계획을 세우는 것입니다. 이때는 주요 시나리오(해피 패스)와 예외적이고 극단적인 상황(언해피 패스/ 에지 케이스)도 모두 포함된 계획을 세워야 합니다.

3.1. 테스트 레시피 작성 방법

작성은 최소한 두 사람이 함께 하는 것이 좋습니다. 한 명은 개발자 관점에서, 다른 한 명은 테스트 관점에서 접근하는 것이 이상적입니다. 만약 테스트 팀이 없다면 개발자 둘이서 진행하되, 둘 중 한 명은 시니어 개발자인 것이 좋습니다. 테스트 레시피를 작성하기 가장 좋은 시점은 기능 작업을 시작하기 직전입니다. 기능의 완료를 판단하는 기준에 테스트 레시피 통과를 포함시키는 것이 좋습니다.

 

일반적으로 저수준 테스트와 고수준 테스트의 비율을 5:1 또는 10:1 비율을 유지하는 것이 좋습니다. 예를 들어, 단위 테스트가 100개 있다면, 통합 테스트는 10개, E2E 테스트는 1개 정도면 충분합니다. 너무 형식적으로 다룰 필요는 없으며, 간단한 시나리오 5~10줄 정도의 텍스트 형식으로 작성하면 충분합니다. 자동화된 방식으로 어떤 수준에서 테스트할 것인지 간략하게 설명한 목록이라고 생각하면 되고, 예시는 아래와 같습니다.

3.1.1. 사용자 프로필 기능에 대한 테스트 레시피 예시

  • E2E: 로그인, 프로필 화면으로 이동, 이메일 업데이트, 로그아웃, 새 이메일로 다시 로그인, 프로필 화면 업데이트 확인
  • API: 더 복잡한 데이터를 사용하여 UpdateProfile API 호출
  • 단위 테스트: 잘못된 이메일로 프로플 업데이트 로직 확인
  • 단위 테스트: 동일한 이메일로 프로필 업데이트 로직 확인
  • 단위 테스트: 프로필 데이터 변환 및 데이터 복원

만약 기능을 켰다가 끄는 기능 토글을 사용하고 있다면, 기능이 꺼져 있다면 해당 기능의 테스트는 실행되지 않도록 설정해줍니다.

테스트 레시피

3.2. 테스트 레시피 작성 규칙

대부분의 테스트를 저수준에서 빠르게 작성하면서, 중요한 시나리오는 고수준에서 커버하며 신뢰도를 얻을 수 있는 테스트 레시피를 작성하려면 아래 규칙을 준수해야 합니다.

  • 속도: 저수준 테스트를 우선시하고, E2E 테스트는 마지막을 고려한다.
  • 신뢰도: 모든 테스트가 통과되었을 때 기능에 대한 확신이 드는 테스트 레시피를 만든다
  • 수정: 테스트를 추가하거나 삭제해도 괜찮으며, 레시피를 함께 작성한 사람에게 반드시 공유한다.
  • 적절한 타이밍: 기능 작업을 시작하기 전에 담당자가 정해진 순간 작성한다.
  • 페어 프로그래밍: 다른 사람과 함께 작성하며, 시나리오 아이디어와 관점을 공유한다.
  • 기존 기능의 테스트를 중복해서 작성하지 말 것: 다른 테스트에서 해당 시나리오를 테스트한 적이 있다면 제거한다.
  • 다른 계층에서 테스트를 중복해서 작성하지 말 것: 동일한 시나리오를 여러 수준의 테스트에서 반복하지 말자.
  • 더 많이, 더 빠르게: 고수준, 저수준 비율을 1:5로 유지하자.
  • 실용성 중시: 모든 수준에 테스트를 작성할 필요는 없다. 기능이 정상동작한다는 확신을 주는 테스트만 실행하면 된다.

4. 배포 파이브라인 관리

4.1. 배포 테스트 그룹

배포 시 진행하는 테스트는 두 그룹으로 나눌 수 있습니다.

4.1.1. 배포 중단 테스트

변경 사항에 문제가 없는지 배포 전에 판단하는 테스트를 말합니다. 단위 테스트, E2E 테스트, 시스템 테스트, 보안 테스트 등이 여기에 속하며, 성공/실패로 결과가 명확합니다.

4.1.2. 참고용 테스트

주요 성과 지표를 파악하고 지속적으로 모니터링하는 데 사용하는 테스트를 말합니다. 결과가 성공/실패로 표현되지 않으며, 고부하 선능 테스트, 장시간 실행되는 비기능적 테스트 등이 포함됩니다. 

4.2. 배포 파이프라인 대 탐색 파이프라인

참고용 테스트가 배포 과정에 직접적인 영향을 미치지 않도록 파이프라인을 두 가지로 나눠서 운영하는 것이 좋습니다. 이때 테스트 결과를 신속하게 얻기 위해 일반적으로 파이프라인의 테스트 계층을 병렬로 실행합니다. 병렬 실행은테스트 실행 시점에서 필요한 환경을 자동으로 생성하고 해제할 수 있는 동적 환경을 활용할 때 큰 효과를 볼 수 있습니다.

4.2.1. 배포 파이프라인

배포를 결정하는 테스트에 사용합니다. 이 파이프라인이 통과한다는 것은 코드를 자동으로 상용에 배포해도 문제가 없다는 것을 의미합니다.

4.2.2.탐색 파이프라인

참고용 테스트에 사용합니다. 이 파이프라인은 배포 파이프라인과 병렬로 실행되지만 배포 기준에는 포함되지 않습니다.

병렬 배포 파이프라인


참고자료