단위 테스트

단위 테스트

단위 테스트가 코드의 정확성을 확인하는 테스트인가? or 단위 기능에 관한 테스트인가?를 명확하게 하기

  • 단위 테스트에는 엄밀한 정의가 없음

  • 애매한 용어이며, 항상 논란이 됨

  • 단위 테스트는 매우 어려움

    • 코드를 처음부터 작성하는것이 아닌 이상 레거시 코드에서 테스트를 작성하는건 매우 어렵다!

  • 코드를 커버하고, 단위 기능을 테스트하는 것은 개발자의 책임

코드 기반 단위 테스트

함수의 커버리지 비율을 측정해 로직의 확실성을 확인하는 화이트 박스 테스트

  • 대부분의 버그를 발견할 수 있는 테스트 방법

화이트 박스 vs 블랙 박스

  • 화이트박스 테스트: 컴포넌트 또는 시스템 내부 구조를 분석하고, 그것을 기반으로 작동을 검사하는 기법

    • 코드 커버리지를 통해 확인하는 테스트

    • 결함 없이 100% 커버되면 테스트 성공으로 간주

    • 커버리지가 부족하면 무언가 결함이 있거나 요구사항을 만족하지 않는것으로 간주

  • 블랙박스 테스트: 내부 구조나 작동원리를 모르는 상태에서 요구사항 사양에 따라 정의된 입력값과 기대값을 통해 컴포넌트 또는 시스템이 사양대로 동작하는지 검사하는 테스트

테스트 항목

입력값의 패턴을 100% 커버하고, 그에 대한 기대 처리가 올바른지를 체크하는 것

  • 프로그램을 실행하는 도중, 시스템상의 이상 작동을 수행하지 않음

    • null pointer, 0으로 나누는 계산 등

  • 입력값과 그에 대응하는 기댓값 출력

  • 모든 분기가 올바르게 처리되는지 확인 (경곗값 테스트)

  • 함수가 그 책임과 의무를 달성하는지도 동시에 주의할 것

  • 단위 수준에서의 처리 기능의 버그를 없애는 것

테스트 커버리지

베이스 코드가 테스트된 비율을 측정하는 지표

  • "커버리지만 신경쓰면 된다" 는 잘못된 사고방식

  • 낮은 코드 커버리지는 넓은 코드 영역이 테스트되어 있지 않음을 보증한다.

  • 높은 코드 커버리지라고 해서 반드시 품질이 높은것은 아니다.

  • 자주 변경되는 코드는 커버해야 한다.

  • 레거시 코드의 커버리지 비율을 방치해도 문제는 없지만, 조금씩 비율을 높여가는 것이 좋다.

명령 커버리지

코드의 실행 가능한 명령문최소 한번은 실행되었는지 확인하는 테스트 커버리지

  • if 자체는 명령문이 아니라, { } 안의 실행 코드만 명령문으로 봄

  • 크게 의미 있는 테스트는 아님

  • 극단적으로 표현할 경우 전혀 도움이 되지 않는다고도 함

    • 모든 조건문이 올바르게 처리되었는지(경곗값 테스트) 측정할 수 없어서

  • 어중간한 명령 커버리지 대신 확실한 조건 커버리지를 달성할 것을 권장

const isPositive = (a: number): boolean => {
  if (a > 0) {
    return true
  }
  
  return false
}
// isPositive(5)로 넣은 경우 5를 넣은 케이스의 한해서 모든 명령문이 실행됨을 체크하는 것

조건 커버리지

각 조건식의 모든 개별 조건들이truefalse인 결과를 적어도 한 번씩 가지는지 확인하는 테스트 커버리지

  • 명령 커버리지의 문제를 해결하는 커버리지 기법

  • 모든 분기가 실행되었는지를 보장하지는 않음

const isValid = (a: number, b: number): boolean => {
  if (a > 0 && b > 0) {
    return true
  }
  
  return false
}
// isValid(1, 1), true true
// isValid(0, 1), false true
// isValid(1, 0), true false
// ----coverage 100%
// isValid(0, 0), false false 테스트 케이스를 작성하지 않아도 커버리지는 충족되는 허점 존재

분기 커버리지

모든 분기(조건문)가 실행되었는지 체크

  • 조건 내부의 각각의 평가값(true/false)를 신경쓰지 않음

  • 복합 조건(&&, ||)이 있는 경우, 일부 조건이 특정 값만 수행되어 충족할 수 있음

  • 그래서 보통 조건 커버리지 + 분기 커버리지를 함께 고려해야 함!

const isValid = (a: number, b: number): boolean => {
  if (a > 0 && b > 0) {
    return true
  }
  
  return false
}
// isValid(0, 0)
// isValid(1, 1)
// ----coverage 100%
// 조건 a > 0와 b > 0가 true, false를 모두 수행했는지는 알 수 없음

기능 단위별 단위 테스트

UI에서 기능(특히 복잡한 기능) 단위로 단위 테스트를 수행

  • 기본적으로 블랙박스 테스트

  • 일반적으로 UI로 부터 무언가를 입력하고 기댓값이 표시되는지를 확인하는 것

기능 단위의 단위 테스트 방법의 기본 요소

  • 단위 기능 경곗값

  • 조합

단위 기능 경곗값

각 기능이 처리할 수 있는 입력값의 경계를 테스트하는 방식

  • 주로 입력값의 최소값, 최대값, 그 근처 값을 사용하여 시스템이 정상적으로 작동하는지 체크

조합

여러 입력값들의 가능한 모든 조합을 테스트하는 방식

  • 반드시 해당 조합에서 버그가 발생하기 쉬운가를 생각하자.

    • 이를 고려하지 않으면 필요한 테스트 케이스 수가 기하급수적으로 늘어남

  • 먼저 데이터가 1 과 2 일 때 확실하게 커버하고, n 이라는 테스트 케이스를 적절한 개수로 한정해 작성할 수 있으면 품질 측면에서는 거의 문제가 없다고 봄

  • 개발자가 생각지도 못한 방식으로 조합해서 버그가 발생하는건 포기하고 발생할 때 마다 케이스를 추가하자.

    • 기능 단위별 단위 테스트에서 조합의 버그 검출 정확도는 현저히 낮다는걸 명심

그래도 버그가 발생하면 어떻게 해야하나?

  • 그런 버그는 우선 발생하지 않는다.

  • 그리고 대부분의 경우 버그는 검출할 수 없다.

블랙박스 테스트 vs 화이트박스 테스트

  • 블랙박스 테스트로 모든 버그를 발견할 수 있다면, 더 큰 비용이 드는 화이트박스 테스트를 수행할 필요는 없다

  • 화이트박스 테스트를 생략하면 일부 버그가 남게되는데 그 정도가 경미한 버그라면 문제 없다.

Last updated