logo
Search검색어를 포함하는 게시물들이 최신순으로 표시됩니다.
    Table of Contents
    10장: 더 나은 테스트 전략 수립

    이미지 보기

    10장: 더 나은 테스트 전략 수립

    테스트 전략 수립하는 방법

    • 25.04.14 작성

    • 읽는 데 21

    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 파이프라인을 짜고 있는데, 염두에 두고 개발해야 할 것 같네요.
    • 테스트 결과를 신속하게 얻는 것은 중요하다.
      • 속도에도 강조를 하고 있어요. 유지가 가능하려면 걸림돌이 되어서는 안 되겠죠. 이 역시 공감합니다.
    • 수동 테스트는 지속 가능하지 않다.

    총평

    테스트 수준에 따라 분류를 깔끔하게 해서 하나하나 설명해주었기에 그 차이를 체감할 수 있었던 장이었습니다.

    각 테스트 수준을 결정하는 기준 역시 명확히 인지시켜주었던 점, 어느 한 쪽에 치우치거나, 분리되는 것, 혹은 과하게 중첩 되는 것을 피하라는 안내는 친절했습니다. 테스트 작성으로 누적이 된다면 추후에 고찰해볼만한 내용이라고 생각했습니다.

    profile

    FE Developer 박승훈

    노력하는 자는 즐기는 자를 이길 수 없다