[F-Lab 모각코 챌린지] 65일차 - 가차없는 테스트

F-Lab 모각코 챌린지 65일차 - 가차없는 테스트. 여러가지 테스트 방법을 소개하고 설명합니다

[F-Lab 모각코 챌린지] 65일차 - 가차없는 테스트

무자비한 테스트

개발자 대부분은 테스트를 싫어한다

코드가 어디에서 깨지는지 무의식적으로 알고 약한 지점을 피해 다니면서, 살살 테스트하려 한다

테스트를 대충 작성하거나 미루다가, 한데 뭉치고 꼬여서 괴물이 되버린 버그에 우리가 만든 프로그램이 잡아먹히는 일이 없어야 할 것이다

그렇게 하기 위해서 우리는 코드를 작성하자마자 테스트해야 한다

일찍 테스트 하고, 자주 테스트해라. 자동으로 테스트해라

버그가 빨리 발견될수록 고치는 비용이 적어진다

코드 조금, 테스트 조금

테스트 코드를 만들기 위해 소요되는 시간에는 그 노력만큼의 가치가 있다

이 외에도 테스트를 통과했다는 것은 그 코드가 완료되었다 고 말할 수 있는 높은 수준의 확신을 갖게 된다

모든 테스트가 통과하기 전엔 코딩이 다 된 게 아니다

무엇을, 어떻게, 언제 테스트할지

무엇을 테스트 할지

테스트의 여섯가지 유형

  1. 단위 테스트 (유닛 테스트)
    - 첫 번째 파라미터가 널 값일 경우 예외 객체를 반환해야 한다
    - 두 번째 파라미터가 널 값일 경우 예외 객체를 반환해야 한다
    - 두 개의 파라미터가 모두 널 값일 경우 예외 객체를 반환해야 한다
    - 파라미터가 정상 범위 안일 경우 작업 실행 후 결과 값을 반환해야 한다
  2. 통합 테스트
    단위 테스트를 우선 수행하여 모듈들이 각각 정상적으로 작동이 되는 것을 확인했다면, 이제 이 모듈들을 연동하여 테스트를 수행하게 되는데 이것이 통합 테스트이다
  3. 유효성 평가 validation 와 검증 verification
    제품의 요구사항이나 설계명세서에 따라 만들어졌는지를 검사하는 것으로, 특정 요구사항이 충족되었음을 객관적인 증거의 제공을 통하여 확인하는 것이다
  4. 자원 고갈, 에러, 그리고 복구
    이상적인 상황 아래서 시스템이 올바르게 작동한다면, 이제 여러가지 실세계의 상황들에서 어떻게 작동할지도 알아야 한다
    우리의 프로젝트는 다음을 포함한 몇 가지 제한 사항에 맞닥뜨릴 것이다
    - 메모리
    - 디스크 공간
    - CPU 대역폭
    - 시간
    - 디스크 대역폭
    - 네트워크 대역폭
    - 칼라 팔레트
    - 해상도
    우리가 만든 앱은 다양한 해상도에서 잘 동작할까?  아카이브가 시작하기 전에 배치 작업이 종료 될까?
    시스템이 실패할 때, 우아하게 실패할 것인가? 최선을 다해 현재 상태를 저장하고 작업 소실을 예방할 수 있을까? 아니면 사용자의 면상에 대고 바로 GPF 나 Core-dump 를 해버릴 것인가?
  5. 성능 performance 테스트
    컴퓨터의 성능을 측정하기 위해 시스템을 실제 사용될 것과 같은 환경에서 작동시켜보는 것
    응답속도나 단위 시간당 일 처리량 등을 측정한다
  6. 사용자 편의성 usability 테스트
    실제 환경의 조건 하에서 실제 사용자들이 시행하는 테스트로, 인간적 요소라는 측면에서 사용편의성을 바라보는 것 이다

어떻게 테스트 할까

회귀 테스트

회귀 테스트는 이전 값과 현재 테스트의 출력을 비교한다

모든 테스트가 사실 회귀테스트 로서 실행될 수 있다

회귀 테스트를 함으로서 오늘 고친 버그가 어제 작동하던 모듈을 망치지 않는다고 확신할 수 있다

테스트 데이터

이런 테스트를 실행하기 위해서는 데이터가 필요한데, 여기에는 두 종류의 데이터가 있다

  • 실세계 데이터
    현실에 기반한 데이터로서 기존 시스템, 경쟁사의 시스템, 혹은 어떤 종류의 프로토타입 등에서 자료를 수집한다
  • 합성 데이터
    어떤 통계적 조건하에서 인공적으로 생성되는 데이터로서 다음 가운데 어떤 이유에서든 합성 데이터를 사용할 수 있다
    - 어떤 실세계 샘플이 제공하는 것 보다도 많은 데이터가 필요하다
    - 경계 조건을 테스트할 데이터가 필요하다 (특정 경계의 날짜, 거대한 레코드 크기, 외국 우편 번호를 포함한 주소 등)
    - 특정한 통계 특성을 보이는 데이터가 필요하다 (세 번에 한 번 꼴로 실패하는 트랜젝션, 이미 정렬된 데이터를 정렬하는 정렬 알고리즘 등)

실패 테스트

완벽한 소프트웨어가 없듯, 테스트도 완벽할 수 없다

버그를 감지해내는 테스트를 작성한 후에, 그 버그가 의도적으로 생기도록 한 다음 테스트가 성공하는지 확인하라

철저히 테스트하기

테스팅은 버그의 존재만 보여줄 수 있지 버그의 부재까지는 보여줄 수 없다
- 다익스트라

코드 커버리지보다 상태 커버리지를 테스트하라

코드 커버리지: 테스트 수행 결과를 정량적인 수치로 나타내는 방법

상태 커버리지:

int test(int a, int b) {
		return a / (a + b)
}

0 ~ 999 사이의 정수 두 개를 받는 함수이다

위 코드는 이론상으로 1,000,000 가지의 논리적 상태를 갖는다

그 가운데 999,999 개는 제대로 작동할 것이고 하나는 그렇지 못할것이다 (a, b 가 모두 0일 때)

언제 테스트 할까

실제 제품에 들어갈 모든 코드는 나오자마자 테스트해야 한다

테스트는 대부분 자동화 되어야 한다. 여기서 중요한 것은, 우리의 '자동화'는 테스트 결과 해석의 자동화 또한 포함한다는 점이다

만약 자동화 될 수 없다면, 해당 태스크에 모든 필요 자원을 투입해서 일정표에 넣어 두어라

그물 조이기

모든 교과서에 적혀 있지만 대다수 프로젝트에서 지켜지지 않는 글귀가 있다

현존하는 테스트의 그물을 빠져 나가는 버그가 있으면, 다음번에는 그걸 잡아낼 수 있도록 새 테스트를 추가해야 한다

버그는 한번만 잡아라

인간 테스터가 버그를 찾아내면, 그 때가 인간 테스터가 그 버그를 찾는 마지막 순간이 되어야 한다

아무리 사소한 것일지라도, 개발자가 "그건 앞으로 절대 다시 일어나지 않을 겁니다" 라고 불평을 하더라도 해당 버그를 확인할 수 있게 자동화 테스트들을 수정해야한다

왜냐면 그런 일은 앞으로 다시 일어날 것이기 때문이다

게다가 우린 자동화 테스트가 우리를 대신해 찾아 줄 버그까지 추격할 시간이 없다

우리는 새 코드를 작성하는 데 시간을 보내야 한다