TOC
참고
본 내용은 단위 테스트의 기술을 읽고 정리한 내용입니다.
책의 내용과 함께 개인적인 의견과 생각, 학습을 담아 작성하였습니다.
테스트 전략
이러한 의문이 자연스럽게 든다.
- 다양한 기능은 어떤 수준에서 검증해야 할까?
- UI 변화, 서버 기능 변경, API 요청, 단위 테스트 등
- 어떤 기능을 어떤 수준에서 테스트할지 어떻게 결정할까?
- 여러 수준에서 중복으로 테스트를 해야 할까?
- E2E 테스트를 더 많이 해야 할까, 아니면 단위 테스트를 더 많이 해야 할까?
- 코드의 신뢰도를 유지하면서 테스트 속도를 어떻게 최적화할 수 있을까?
- 각 테스트 유형은 누가 작성해야 할까?
답은 테스트 전략에 있으며, 첫 단계는 테스트 전략의 범위를 다양한 테스트 유형으로 나누는 것
일반적인 테스트 유형과 수준
서비스 도메인에 따라 테스트 유형과 수준이 다를 수 있다.
높은 테스트 수준
현상
: 실제 의존성을 더 많이 사용장점
: 전체 시스템 정확성에 대한 신뢰도는 높음단점
: 테스트는 더 느리고 불안정해짐예시
: E2E/UI 통합, 격리 테스트
낮은 테스트 수준
현상
: 실제 의존성을 덜 사용장점
: 테스트는 더 빠르고 생산적, 안정적, 높은 유지보수성단점
: 전체 시스템에 대한 신뢰도는 낮음예시
: 인메모리 단위, 컴포넌트 테스트

다이어그램을 어떻게 활용할 수 있을까?
- 어떤 테스트를 작성할지 결정하는 데 필요한 의사 결정 프레임워크 설계시 사용 가능
- 테스트를 작성할 때 고려행 할 여러 기준 설정
- 기준에 따라 어떤 테스트 유형을 선택할지 판단할 수 있도록 하는 것
- 최적의 테스트 유형을 선택할 수 있도록 하는 도구
테스트 평가 기준
여러 선택지 중 결정을 내리는 데 가장 우선시 해야 하는 것은 명확한 기준을 세우고, 선택지를 좁혀 나가는 것
일반적인 테스트 평가표
- 복잡성(complexity)
- 테스트 작성, 읽기, 디버깅 과정의 복잡도
- 낮을수록 좋음
- 불안정성(flakiness)
- 제어할 수 없는 요소 때문에 테스트가 실패할 가능성
- ex. 다른 팀의 코드, 네트워크, DB, 설정 권한 등
- 낮을수록 좋음
- 테스트를 통과할 때 신뢰도(confidence)
- 테스트 통과가 개발자에게 얼마나 큰 신뢰를 주는지
- 높을수록 좋음
- 유지보수성(maintainability)
- 테스트를 얼마나 자주 변경해야 하는지, 변경이 얼마나 쉬운지
- 높을수록 좋음
- 실행 속도(speed)
- 테스트가 얼마나 빨리 끝나는지
- 높을수록 좋음
테스트 수준에 따른 분류
단위 테스트
- 메모리에서 실행
- 사용하는 모든 요소에 대한 제어권 보유
컴포넌트 테스트
- 환경과 상황은 단위 테스트와 동일
- 단, 더 많은 함수나 클래스, 컴포넌트를 작업 단위 내에 포함
- 다시 말해, 진입점과 종료점 사이에 더 많은 '요소' 포함
통합 테스트
- 일반적인 단위 테스트와 거의 동일
- 단, 일부 의존성은 스텁으로 대체 불가
- 예를 들어, 실제 환경 설정, DB, 파일 시스템 등을 사용하는 경우
API 테스트
- 이전까지는 배포나 실행없이 테스트 가능, 하지만 여기부턴 아님
- 메모리 내부가 아니라 작업 프로세스 외부에서 동작
- 더이상 테스트 대상을 가짜로 만들지 않음
- 실제 환경 이라는 새로운 의존성 추가
E2E/UI 격리 테스트
- 사용자 관점에서 애플리케이션 테스트
격리
: APP, 서비스 등 우리의 테스트 대상만 테스트함을 의미- 즉, 나머지는 가짜로 만들어 사용
E2E/UI 시스템 테스트
- 여기부턴 가짜인 것이 없음
- 실제 운영 환경과 동일한 조건을 갖춘다.
- 목적 : 전체 시스템이 제대로 동작하는지 검증
각 테스트 수준마다 존재하는 안티 패턴
E2E 테스트만 사용하는 안티 패턴
왜 안티패턴일까?
- 유지보수와 디버깅이 어려움
- 느리고 불안정성이 높음
- 비용은 계속 발생하지만, 새로운 E2E테스트를 작성하는 것에서 얻는 가치는 감소
E2E 테스트의 가치 하향
- 처음엔 큰 신뢰감
- 여러 경로에서 불러온 코드를 한 번에 확인
- 글루 코드(glue code)도 함께 검증하기 때문
- (글루 코드 : APP과 다른 시스템의 연결부를 관리하는 코드)
- 하지만 두 번째 E2E 테스트부터는 훨씬 적은 신뢰도
- 과정과 결과에 큰 차이가 없기 때문(외부 의존성도)
- 첫 테스트에서 얻을 수 있는 가치 대비 두 번째 테스트는 상대적으로 작음
- E2E 테스트는 글루코드의 검증을 하는 첫 테스트가 대부분의 신뢰도
빌드 분석자
- 고수준 테스트는 불안정성이 높아 여러 이유로 실패함
- 일부는 실제 테스트와 관련 없는 이유 때문에
- 따라서 실패 이유를 체계적으로 분석, 중요성을 확인해야 함
빌드 분석의 고충을 해결하는 방법
- 탄탄하게 설계된 자동화 테스트 파이프라인 구축하기
- 불안정한 테스트가 있어도 빌드 성공 여부를 자동으로 판단 가능
책임 전가 사고방식
- E2E 테스트만으로 운영하면 보통 모니터링 책임은 QA팀에 있음
- 개발자들이 빌드 결과에 관심을 가지지 않거나, 결과를 알지 못함
- 테스트를 본인의 소유물로 생각하지 않음
안티패턴이 생기는 이유
- 업무 분리
- 많은 조직이 QA팀과 개발팀을 별도로 운영
- 각 팀이 별도의 파이프라인을 관리
- 잘 되고 있으면 굳이 바꾸지 말자는 사고방식
- E2E 테스트를 시작해서 결과가 좋으면 새로운 테스트도 계속 같은 방식으로 작성
- 테스트 실행 시간이 너무 길어지면 이미 늦음
- 매몰 비용의 오류
- E2E 유형의 테스트를 많이 작성했는데 바꾸거나 제거하면 비용이 많이 든다고 착각
- 테스트를 유지하고 디버깅하며 실패 원인을 파악하는 데 드는 시간과 비용이 더 큼
- 기본적인 시나리오만 남기고 테스트를 삭제하는 게 더 효율적
E2E 테스트는 하지 말아야 할까
- 그렇지만은 않다. E2E 테스트는 피할 수 없다.
- 애플리케이션이 제대로 동작한다는 확신과 신뢰감을 준다는 장점
- 단, 테스트의 수를 최소화할 것.
저수준 테스트만 사용하는 안티 패턴
현상
- 저수준 테스트 안티 패턴은 대부분의 자동화 테스트가 저수준 테스트에 집중
- 일부의 통합 테스트, 하지만 E2E는 거의 없음
- 가장 큰 문제는 테스트가 통과되더라도 애플리케이션의 정상 동작을 신뢰하지 않음
- 결국 테스트 이후에도 수동 디버깅과 테스트를 수행
원인
- 개발자가 저수준 테스트만 작성하는 데 익숙함
- 개발자가 고수준 테스트 작성에 자신이 없음
- 고수준 테스트는 QA 팀이 해야 할 일이라고 생각할 때문
저수준 테스트와 고수준 테스트의 단절
테스트가 단절되어 있으면 예상치 못한 오류나 비효율성이 따라옴
- 여러 테스트가 여러 수준에서 반복적으로 생김
- 테스트 종류별 작성자가 다름
- 서로의 테스트에는 관심이 없음
- 서로 다른 파이프라인에서 각기 다른 유형의 테스트가 실행될 가능성이 높음
- 상위 수준과 하위 수준 테스트의 문제점이 모두 드러남
테스트 레시피 전략
테스트 레시피
: 특정 기능을 어떻게 테스트할지 간단한 계획을 세우는 것- 주요 시나리오(해피 패스)뿐만 아니라, 예외적인 상황과 극단적 상황(엣지 케이스)도 모두 포함해야 함
테스트 레시피 작성 방법
- 최소한 두 사람이 함께 작업하면 좋음
- 각각 개발자 관점, 테스터 관점에서 접근
- 작성하기 가장 좋은 시점 : 기능 작업을 시작하기 직전
- 테스트 레시피가 해당 기능의 '완료' 기준에 포함
- 전체 테스트 레시피가 통과해야 기능이 완성되었다고 간주
- 레시피는 시간에 따라 바뀔 수 있음
- 고정된 산출물이 아니라, 소프트웨어 개발처럼 계속해서 발전을 거듭하는 작업물
- 기능이 제대로 작동한다는 '신뢰감'을 주는 시날리오 목록
- 일반적으로 저수준:고수준 비율은 1:5에서 1:10
- 단위 테스트가 100일 때, 통합 테스트는 10, E2E는 1
테스트 레시피를 인식하기
- 너무 형식적으로 다루지 않기
- 공식적인 자리에서 발표 보고서로 사용하지 않기
- 관련자에게 무언가를 약속하는 문서로 사용하지 않기
- 필요에 따라 추가, 수정, 삭제를 할 수 있는 주석 정도로 여기기
테스트 종류에 따른 레시피 작성
사용자 프로필 기능을 예시로 살펴보자.
- E2E
- 로그인, 프로필 화면으로 이동,
- 이메일 업데이트, 로그아웃
- 새 이메일로 다시 로그인, 프로필 화면 업데이트
- API: 더 복잡한 데이터를 사용하여 UpdateProfile API 호출
- 단위 테스트
- 잘못된 이메일로 프로필 업데이트 로직 확인
- 동일한 이메일로 프로필 업데이트 로직 확인
- 프로필 데이터 변환 및 데이터 복원
언제 사용해야 할까
- 어떤 시나리오를 테스트할 지 구상할 때
- 어느 수준에서 테스트하면 가장 적합할지 결정할 때
테스트 레시피 작성 규칙
- 속도: 저수준 테스트를 우선시 할 것
- 신뢰도: 모든 테스트가 통과되었을 때 기능이 잘 동작한다고 확신할 수 있을 것
- 수정: 코딩하면서 테스트를 추가하거나 삭제해도 된다. 단, 레시피 동료들에겐 알릴 것
- 적절한 타이밍: 누가 코딩할지 정해졌을 때 작성할 것
- 페어 프로그래밍: 사람마다 다른 테스트 아이디어와 관점을 공유할 것
- 기존 기능의 테스트를 중복하지 말 것
- 다른 계층에서 테스트를 중복해서 작성하지 말 것
- 더 많이, 더 빠르게: 테스트 수준간 비율을 준수할 것(1:5 ~ 1:10)
- 실용성 중시: 중요한 점은 레시피에 있는 모든 시나리오가 통과했을 때 기능이 제대로 작동한다고 느낄 수 있어야 한다는 것
배포 파이프라인 관리
이런 궁금증이 있다.
- 성능 테스트는 언제 해야 할까?
- 보안 테스트나 부하(load) 테스트는 언제 해야 할까?
- 실행하는 데 시간이 오래 걸리는 여러 테스트는 어디에서 언제 실행해야 할까?
- 각 테스트는 어느 단계에 속할까?
- 배포 파이프라인에 포함시켜야 할까?
이런 질문들에 대해 그룹을 나눠보자.
- 배포 중단 테스트
- 변경 사항에 문제가 없는지 배포 전에 판단
- 단위 테스트, E2E 테스트, 시스템 테스트, 보안 테스트 등
- 보통 결과가 명확: 통과하면 문제가 없고, 실패하면 코드 수정 후 배포 가능
- 참고용 테스트
- 주요 성과 지표(KPI)를 파악하고 지속적으로 모니터링하는 데 사용
- 코드를 분석하고 복잡도를 스캔
- 고부하 성능 테스트, 장기간 비기능적 테스트 등
- 결과가 '성공' 또는 '실패'로 나뉘지 않음
- 소프트웨어 배포에는 큰 영향을 주지 않음
배포 파이프라인 vs 탐색 파이프라인
참고용 테스트가 배포 과정에서 중요한 결과를 확인하는 데 걸리는 시간을 늘리지 않도록 배포에 직접적인 영향을 미치는 테스트와 그렇지 않은 테스트를 구분하여 두 가지 파이프라인으로 운영해야 한다.
배포 파이프라인
- 배포를 결정하는 테스트에 사용
- 이 파이프라인이 통과하면 코드를 production에 배포해도 된다는 확신을 가질 수 있어야 함
- 비교적 빠르게 실행 결과를 알려 줄 수 있어야 함
목적
: 코드가 문제없이 통과될 경우 배포를 진행
탐색 파이프라인
- 참고용 테스트에 사용
- 배포 파이프라인과 병렬로 실행
- 지속적으로 작동
- 배포 기준에는 포함하지 않음
- 오래 걸려도 무방(실행 결과를 기다리지 않음)
목적
: 리팩터링해야 하는 코드를 탐색 (복잡도, 성능 등)
테스트 계층 병렬화
- 테스트 결과를 신속하게 얻는 것은 중요하다.
- 각 단계별 결과를 빠르게 받기 위해 여러 테스트 계층을 병렬로 실행하라.
- 동적 환경을 활용할 때 큰 효과를 얻을 수 있다.

정리
기억할 내용
- 테스트에서 중요한 점은 레시피에 있는 모든 시나리오가 통과했을 때 기능이 제대로 작동한다고 느낄 수 있어야 한다
- 이전 챕터에서 이야기한 신뢰도가 또 한 번 강조됐네요. 공감합니다.
- 배포에 직접적인 영향을 미치는 테스트와 그렇지 않은 테스트를 구분하여 두 가지 파이프라인으로 운영해야 한다.
- 요새 CI/CD 파이프라인을 짜고 있는데, 염두에 두고 개발해야 할 것 같네요.
- 테스트 결과를 신속하게 얻는 것은 중요하다.
- 속도에도 강조를 하고 있어요. 유지가 가능하려면 걸림돌이 되어서는 안 되겠죠. 이 역시 공감합니다.
- 수동 테스트는 지속 가능하지 않다.
총평
테스트 수준에 따라 분류를 깔끔하게 해서 하나하나 설명해주었기에 그 차이를 체감할 수 있었던 장이었습니다.
각 테스트 수준을 결정하는 기준 역시 명확히 인지시켜주었던 점, 어느 한 쪽에 치우치거나, 분리되는 것, 혹은 과하게 중첩 되는 것을 피하라는 안내는 친절했습니다. 테스트 작성으로 누적이 된다면 추후에 고찰해볼만한 내용이라고 생각했습니다.