Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

eumjo_o

Unit Testing 4장 본문

카테고리 없음

Unit Testing 4장

eumjo_o 2023. 2. 27. 23:26

4장 좋은 단위 테스트의 4대 요소

가치있는 테스트를 어떻게 식별할 수 있을까??

4.1 좋은 단위 테스트의 4대 요소 자세히 살펴보기

  • 회귀 방지
  • 리펙터링 내성
  • 빠른 피드백
  • 유지 보수성

4.1.1 첫번째 요소: 회귀 방지

💡 회귀: 코드를 수정한 후 기능이 의도한 대로 작동하지 않는 경우 회귀방지: 테스트가 얼마나 **버그(회귀)**의 존재를 잘 나타내는지에 대한 척도

 

  • 코드는 자산이 아니라 책임!!!

테스트에서 버그(회귀)가 드러날 확률이 높아지는 원인 3가지

  • 테스트 중에 실행되는 코드의 양
  • 코드 복잡도
  • 코드의 도메인 유의성

4.1.2 두 번째 요소: 리펙터링 내성

  • 리펙터링을 통해 코드의 비기능적 특징을 개선하는 것으로 가독성을 높이고 복잡도를 낮추는 것

💡 거짓 양성(false positive) 실제로 기능이 의도한 대로 작동하지만 테스트는 실패를 나타내는 것

 

  • 허위 경보에 익숙해지고 주의를 기울이지 않기 때문에 코드 문제에 대응하려는 능력과 의지가 희석된다.
  • 테스트를 신뢰할 수 있는 안전망으로 인식하는 것이 줄어들고 테스트 스위트에 대한 신뢰를 잃게 된다.
  • 거짓 양성은 프로젝트 초기에 부정적인 영향을 미치지는 않지만 프로젝트가 성장함에 따라 점점 더 중요해진다. 즉 거짓 음성(알려지지 않은 버그)만큼 중요하다.

💡 리팩터링 내성 테스트가 거짓 양성을 내지 않고(테스트를 빨간색으로 바꾸지 않고) !! 애플리케이션 코드 리펙터링을 유지할 수 있는 정도를 의미

 

4.1.3 무엇이 거짓 양성의 원인인가?

  • 거짓 양성의 수는 테스트 대상 시스템의 내부 구현 세부 사항과 테스트 간의 강결합의 결과.
  • 결합도를 낮추려면 테스트는 SUT가 수행한 단계가 아니라 SUT가 만든 최종 결과를 검증해야 한다.
  • 거짓 양성이 생길 가능성을 줄이는 방법은 해당 구현 세부사항에서 테스트를 분리하는 것 뿐이다.

똑같이 적용할 수 있는 다른 구현을 고려하지 않고 특정 구현만 예상해서 알고리즘을 검사한다.

4.1.4 구현 세부 사항 대신 최종 결과를 목표로 하기

  • 리팩터링 내성을 높이는 방법은 SUT의 구현 세부사항과 테스트 간의 결합도를 낮추는 것.

좋은 테스트: “최종 결과가 올바른가?” 좋지 않은 테스트: “이 모든 단계가 올바른가?”

4.2 첫 번째 특성과 두 번째 특성 간의 본질적인 관계

  • 회귀방지와 리펙터링 내성은 테스트 정확도에 기여한다. 테스트는 가능한 한 적은 소음(거짓 양성)으로 강한 신호(버그를 찾을 수 있음. 회귀 방지 영역)를 발생시키기 때문에 정확하다.

4.2.1 테스트 정확도 극대화

오류 유형 표 작동 고장

테스트 통과 올바른 추론 (참 음성) 2종 오류(거짓 음성) ⇒ 회귀 방지
테스트 실패 1종 오류(거짓 양성) ⇒ 리펙터링 내성 올바른 추론 (참 양성)
  • 정확도 지표
    • 테스트가 버그 있음을 얼마나 잘 나타내는가 (== 거짓 음성(회귀 방지 영역) 제외)
    • 테스트가 버그 없음을 얼마나 잘 나타내는가 (== 거짓 양성 (리펙터링 내성 영역) 제외)

테스트 정확도 = 신호(발견된 버그 수) / 소음 (허위 경보 발생 수)

  • 테스트의 정확도를 높이는 방법
    • 분자, 신호를 증가시키는 것 , 회귀를 더 잘 찾아내는 테스트로 개선하는 것
    • 분모, 소음을 줄이는 것, 허위 경보를 발생시키지 않는 테스트로 개선하는 것
  • 가능한 한 소음(허위 경보)이 적은 강한 신호(버그를 찾을 수 있음)를 생성할 때,

4.2.2 거짓 양성과 거짓 음성의 중요성: 역학 관계

  • 거짓 양성(허위 경보)은 초기에 그다지 부정적인 영향을 미치지 않는다.
    • 초기에는 리팩터링이 바로 중요하지 않고, 시간이 흐를 수록 코드베이스는 복잡해지고 체계적이지 않게 된다.
  • 그러나 프로젝트에 거짓 음성(알려지지 않은 버그)이 중요한 만큼 거짓 양성도 점점 더 중요해진다.
    • 코드 베이스가 나빠질수록 새로운 기능에 드는 비용이 커지기 때문에 리팩터링이 점점 필요해지고, 이에 따라 테스트에서 리팩터링 내성도 중요해진다.

4.3 세 번째 요소와 네 번째 요소: 빠른 피드백과 유지 보수성

  • 빠른 피드백
    • 테스트 속도가 빠를수록 테스트 스위트에서 더 많은 테스트를 수행할 수 있고 더 자주 실행할 수 있다.
    • 테스트가 빠르게 실행되면 코드에 결함이 생기자마자 버그에 대해 경고하고 버그를 수정하는 비용을 줄일 수 있다.
  • 유지 보수성
    1. 테스트가 얼마나 이해하기 어려운가
      • 테스트는 코드 라인이 적을수록 더 읽기 쉽다. 하지만 인위적으로 압축하지는 말아야 한다.
      • 테스트 코드의 품질은 제품 코드만큼 중요하고, 테스트를 작성할 때 절차를 생략하지 말아야 한다.
      • 테스트 코드를 일급 시민(first-class citizen)으로 취급하라
    2. 테스트가 얼마나 실행하기 어려운가
      • 테스트에 관련된 프로세스 외부 의존성은 적을수록 쉽게 운영할 수 있다.
      • 테스트가 프로세스 외부 종속성으로 작동하면, 데이터베이스 서버를 재부팅하고 네트워크 연결 문제를 해결하는 등 의존성을 상시 운영하는 시간을 들여야 한다.

4.4 이상적인 테스트를 찾아서

테스트의 가치 = 회귀 방지 X 리펙터링 내성 X 빠른 피드백 X 유지 보수성

  • 어떠한 특성이라도 0이 되면 전체가 0이 된다.
  • 가치가 있으려면 테스트는 네 가지 범주 모두에서 점수를 내야 한다.
  • 가치 추정치는 [0..1] * [0..1] * [0..1] * [0..1]
  • 테스트 코드를 포함한 모든 코드는 책임(liability)이고, 최소한으로 필요한 가치로 임계치를 상당히 높게 설정하고, 이 임계치를 충족하는 테스트만 테스트 스위트에 남긴다.

4.4.1 이상적인 테스트를 만들 수 있는가?

  • 이상적인 테스트는 각 속성마다 모두 1을 갖는 것
  • 회귀 방지, 리팩터링 내성, 빠른 피드백은 모두 상호 배타적이기 때문에, 세 가지 특성 모두 최대로 하는 것은 불가능하고, 셋 중 하나를 희생해야 나머지 둘을 최대로 할 수 있다.

4.4.2 극단적인 사례1: 엔드 투 엔드 테스트

  • 많은 코드를 테스트하므로 회귀 방지를 훌륭하게 해낸다.
  • 거짓 양성에 면역이 돼 리팩터링 내성도 우수하다.
  • 하지만, 엔드 투 엔드 테스트에만 의존하는 모든 시스템은 피드백을 빨리 얻기가 어렵고, 느리다.

4.4.3 극단적인 사례2: 간단한 테스트

  • 간단한 테스트는 매우 빠르게 실행되고 빠른 피드백을 제공한다.
  • 거짓 양성이 생길 가능성이 상당히 낮기 때문에 리펙터링 내성도 우수하다.
  • 간단한 테스트는 우수한 리팩터링 내성과 빠른 피드백을 제공하지만 회귀 방지가 없다.

4.4.4 극단적인 사례3: 깨지기 쉬운 테스트

  • 깨지기 쉬운 테스트(Britte test)
    • 실행이 빠르고 회귀를 잡을 가능성이 높지만 거짓 양성이 많은 테스트를 작성하기가 매우 쉽다.
  • 깨지기 쉬운 테스트는 빠르게 실행되고 회귀 방지를 훌륭히 해내지만, 리팩터링 내성은 거의 없다.

4.4.5 이상적인 테스트를 찾아서: 결론

  • 좋은 단위 테스트의 처음 세 가지 특성(회귀방지, 리팩터링 내성, 빠른 피드백)은 상호배타적
  • 세 가지 특성 중 두 가지를 극대화하는 테스트를 만들기는 쉽지만 나머지 특성 한가지를 희생해야 하고, 세 가지 특성 모두 완벽한 점수를 얻어서 이상적인 테스트를 만드는 것은 불가능하다.
  • 엔드 투 엔드 테스트는 관련된 모든 의존성을 설정해야 하므로 계속 운영하려면 추가적인 노력이 더 들고, 유지비 측면에서 더 비싼 경향이 있다.
  • 최상의 테스트는 유지 보수성과 리팩터링 내성을 최대로 갖기 때문에 항상 이 두 특성을 최대화하도록 노력해야 한다.
  • 회귀 방지, 리팩터링 내성, 빠른 피드백의 상호 배타성 때문에 세 가지 특성 모두를 양보할 만큼 서로 조금씩 인정하는 것이 최선의 전략이다.
  • 테스트가 얼마나 버그를 잘 찾아내는지(회귀방지)와 얼마나 빠른지(빠른 피드백) 사이의 선택으로 절충이 귀결된다.
  • 테스트 스위트를 탄탄하게 만들려면 테스트의 불안정성(거짓 양성)을 제거하는 것이 최우선 과제

 💡 CAP 정리

일관성(consistency) - 모든 읽기가 가장 최근의 쓰기 또는 오류를 수신하는 것을 의미

가용성(availability) - 모든 요청 (시스템 내 전체 노드 중단은 제외하고) 응답을 수신하는 것을 의미

분할 내성(partition tolerance) - 네트워크 분할(네트워크 노드 간 연결 끊김)에도 시스템이 계속 작동함을 의미

일관성과 가용성 간의 절충

 

4.5 대중적인 테스트 자동화 개념 살펴보기

  • 테스트 피라미드와 화이트박스 테스트 대 블랙박스 테스트
  • 테스트를 작성할 때는 블랙박스 테스트 방법
  • 테스트를 분석할 때는 화이트박스 방법

4.5.1 테스트 피라미드 분해

테스트 스위트에서 테스트 유형 (단위 테스트, 통합 테스트, 엔드 투 엔드 테스트) 간의 일정한 비율을 일컫는 개념

  • 엔드 투 엔드 테스트 < 통합 테스트 < 단위 테스트

4.5.2 블랙박스 테스트와 화이트박스 테스트 간의 선택

💡 블랙박스 테스트 (Black-box testing) - 시스템의 내부 구조를 몰라도 시스템의 기능을 검사할 수 있는 소프트웨어 테스트 방법.

 

💡 화이트박스 테스트(White-box testing) - 애플리케이션의 내부 작업을 검증하는 테스트 방식. 테스트는 요구 사항이나 명세가 아닌 소스 코드에서 파생된다.

 

회귀 방지 리팩터링 내성

화이트박스 테스트 좋은 나쁨
블랙박스 테스트 나쁨 좋음
  • 검증문을 작성할 때, 제품 코드에 의존하지 말아야 한다.
  • 테스트에서 별도의 리터럴과 상수집합을 사용하고, 필요시 리터럴과 상수를 복제해야 한다.
  • 테스트는 제품 코드와 독립적으로 검사점을 제공해야 하고, 그렇지 않을 시 동어반복 테스트( 아무것도 검증하지 않고, 무의미한 검증문만 있는 테스트)를 만들 위험이 있다.