테스트하기 쉬운 코드 [실용주의 프로그래머]

LEE KYU WON·2021년 2월 9일
0

테스트 가능성이 높은 코드는 디자인이 좋다. 재미있게도, 디자인을 잘 만들려고 할 때보다 테스트 가능성을 높이려고 했을 때 결과 코드의 디자인이 더 나은 경우가 많다.
by 김창준

단위 테스트

유닛테스트에 관한 메타포로 IC칩을 예로 들기 적절하다. IC 칩은 테스트 할 수 있게 설계된다. 공장이나 회로에 설치될 때 뿐만 아니라 제품으로 배포된 현장에서도 테스트 할 수 있도록 설계단계부터 고려된다.

소프트웨어에서도 똑같은 것을 할 수 있다. 처음에 설계 할 떄 부터 테스트 가능성을 열어두고 코드를 서로 연결하기 전에 코드를 하나하나 철저하게 테스트 해야만 한다.

유닛테스트를 할 경우 각 모듈의 동작을 검증하기 위해 다른것들과 분리 시켜놓고 테스트가 이루어진다. 모듈을 통제된 환경에서 테스트하면 더 넓은 환경에서 어떻게 작동할 것인지 더 감을 잡을 수 있다. 일종의 인위적인 환경을 구축한 다름, 테스트 할 모듈의 루틴을 호출한다. 그런다음 반환된 결과들을 이미 알고있는 값과 비교하던지 이전에 나온 값과 같은지 비교하는 회귀 테스트를 진행한다.

계약을 잘 지키는지 테스트 해보기

계약에 의한 설계를 할 경우 테스트 하기가 수월하다. 어떤 단위가 가지가 맺은 계약을 존중하는지 보유줄 확실한 테스트 케이스를 만들면 된다.

테스트는 두가지 의미를 갖는다.

  1. 코드가 계약을 지키는지 확인한다.
  2. 코드로 표현 된 계약의 의미가 우리가 생각한 것과 일치하는지 여부.

만약 다음과 같이 제곱근을 구하는 계약이 있을때의 테스트 케이스는 다음과 같다.

require
  argument >= 0;
ensure
  abs(res*res - argument) <= epsilon*argument

Test case

  • 음수를 인자를 주고 거부되는지
  • 0(boundary value) 를 인자로 주고 받아들이는지
  • 결과의 제곱과 원래 인자의오차가 특정 비율 이하인지 확인한다.

다른 모듈에 의존하는 모듈의 테스트

linkedList 모듈과 Sort 모듈을 의존하는 모듈 A 가 있다면, 우리는 다음과 같은 순서로 테스트 해야 한다.

  1. LinkedLsit 의 계약을 완전히 테스트한다.
  2. Sort 의 계약을 완전히 테스트한다.
  3. A 의 계약을 테스트한다. A의 계약은 나머지 모듈의 계약에 의존하지만 직접적으로 그 사실을 표현하고 있지는 않다.

이런 방식을 사용하면 하위 모듈의 에러를 바로 감지 할 수 있고 LinkedList 모듈과 Sort 모듈의 테스트에 문제가 없는데 A 의 테스트가 실패한다면, A 모듈에 문제가 있거나 A가 하위 모듈을 사용하는 방식에 문제가 있다고 확신 할 수 있다.

단위테스트를 작성하는 방법

단위테스트 작성 시 가장 고려해야 할 부분은 단위 테스트가 소스코드 구석의 외진곳에 박혀있으면 안된다는 점이다. 단위테스트는 찾기 편한곳에 있어야 한다.

프로젝트 규모가 작다면 그 모듈안에 테스트코드를 직접 삽입 할 수 있고 규모가 크다면 각각의 테스트를 하위 디렉토리로 옮겨놓을 수 있다.

테스트 코드를 접근하기 쉽게 하는것은 다음과 같은 장점을 갖는다.

  1. 모듈의 모든 기능을 어떻게 사용해야 하는지 보여주는 예제
  2. 코드를 변경 시 검증하기 위한 회귀 테스트를 구축 할 수 있는 수단

테스트 장치 사용하기

테스트 코드를 단순히 모듈 안에 작성하는 방법은 간단하지만 더 많은 기능들이 필요할 때가 있다. 이땐느 테스트 장치를 사용할 수 있다. (웹의 경우 Jest 등)

테스트 장치는 다음과 같은 기능을 지원해야 한다.

  • 시작할 때 할 일 (setup) 과 마칠 때 할 일 (cleanup) 을 지정할 수 있는 표준적인 방법
  • 개별적인 테스트들을 선택하거나, 아니면 모든 테스트를 한꺼번에 선택하게 해주는 메서드
  • 예상된 결과(expected) 와 결과(result) 를 비교 하는 방법
  • 실패를 보고하는 표준화된 형태

테스트 윈도우 만들기

아무리 테스트 집합이 좋더라도 모든 버그를 발견할 가능성은 없다. 배포된 현장에서 숨어있던 버그가 나올 수 있다.

따라서 배포된 상태에서 소프트웨어 안에서 흐르는 데이터의 상태를 조회할 수 있는 기능을 만들 수 있다.

로그파일을 만드는것도 이런 메커니즘 가운데 하나이다. 로그메시지는 규칙적이고 일관된 형식이야 한다. 프로그램의 처리 시간이나 프로그램의 로직을 추론하기 위해 기록을 파싱할 수 있기 때문이다. 형식이 좋지 않거나 일관적이지 않은것은 그냥 "뱉어낸 것" 에 불과한다.

또다른 방법은 진단 제어창에 접근 할 수 있는 단축키를 제공하는 것이다. (크롬 개발자도구를 생각 할 수 있다.)

더 크고 복잡한 규모의 서버 코드라면 웹 서버를 내장시키는것이 좋은 방법이다. 특정 포트를 디버깅 포트로 지정해서 접근하면 내부 상태와 로그 항목을 조회하도록 할 수 있다. 이 방법은 어려워보이지만 자유롭게 사용 할 수 있는 오픈소스가 있다고 한다.

테스트 문화

테스트는 기술적이라기 보다는 문화적인 것이다. 테스트 스크립트를 만들 수 있는 간단한 명령어를 만들고 commit 하기 전에 테스트를 강제하도록 하는 등 팀 내에 테스트 문화가 정착하도록 해야한다.

profile
벼랑끝에서 성장하는 개발자 이규원입니다.

0개의 댓글