좋은 단위 테스트(unit test)의 원칙
- 대부분의 코드는 격리되어 있지 않고, 다른 수많은 코드에 의존한다.
- 어떤 개발자들은 단위 테스트에서 테스트 대상이 되는 코드가 의존하는 코드를 차단하려고도 하는 반면, 다른 개발자들은 의존하는 코드를 포함해서 테스트하는 것을 선호한다.
단위 테스트 기초
- 테스트 중인 코드
- 실제 코드라고도 하고, 테스트의 대상이 되는 코드를 의미한다.
- 테스트 코드
- 단위 테스트를 구성하는 코드를 가리킨다.
- 테스트 케이스
- 특정 동작이나 시나리오를 테스트한다.
- 테스트 케이스는 아래와 같은 섹션으로 나누어진다.
- 준비 (given) : 테스트하기 위한 특정 동작을 호출하기 위해 몇가지 설정을 수행하는 것
- 실행 (when) : 테스트 중인 동작을 실제로 호출하는 코드
- 단언 (then) : 테스트 중인 동작이 실행되고 나면 실제로 올바른 일이 발생했는지 확인하는 것
좋은 단위 테스트란?
훼손의 정확한 감지
- 단위 테스트의 가장 명확하고 주된 목표는 코드가 훼손되지 않았는지 확인하는 것
- 즉, 코드가 의도된 대로 수행하며 버그가 없다는 것을 확인하는 것이다.
- 이러한 훼손의 정확한 감지는 아래와 같은 이점을 준다.
- 코드에 대한 초기 신뢰를 준다.
- 미래의 훼손을 막아준다.
- 플래키란?
- 테스트 대상 코드가 정상임에도 불구하고 때로는 통과하고 때로는 실패하는 테스트를 일컫는다.
- 무작위성, 타이밍 기반 레이스 조건, 외부 시스템에 의존하는 등의 테스트의 비결정적 동작에 기인한다.
세부 구현사항에 독립적
개발자가 코드베이스에 가할 수 있는 변경은
- 기능적 변화
- 리팩토링
이 두가지이다.
이 중에서 리패토링을 했을 때, 테스트코드에 변경사항이 생겨서는 안된다.
테스트가 세부구현사항에 독립적일 수록 리팩터링에 대한 확신을 갖기 쉽다. 테스트가 여전히 잘 통과된다면 리팩터링이 잘 된 것이고, 테스트가 실패한다면 코드의 동작을 변경한 실수를 범한 것이다.
잘 설명되는 실패
- 하나의 테스트 케이스는 한 가지 사항만 검사해야한다.
- 각 테스트 케이스에 대해 서술적인 이름을 사용해야한다.
이해 가능한 테스트 코드
- 한번에 너무 많은 것을 테스트하려고 하지 말 것
- 너무 많은 공유 테스트 설정을 하지 말 것
- 테스트 코드를 이해하기 쉽게 만들어야하는 이유는, 일부 개발자들이 테스트코드를 코드에 대한 일종의 사용설명서로 사용하기 때문이다.
쉽고 빠른 실행
- 테스트가 느리면, 테스트가 힘든 작업이 되고, 테스트가 힘들면 하고싶지 않아진다.
퍼블릭 API 에 집중하되, 중요한 동작은 무시하지 말라
- '퍼블릭 API 만을 사용한 테스트'는 단위 테스트와 관련하여 매우 일반적인 조언이다.
- 퍼블릭 API에 초점을 맞추면 세부 사항이 아닌 코드 사용자가 궁극적으로 신경 쓸 동작에 집중할 수 밖에 없게 되는데, 세부사항은 목적을 이루기 위한 수단일 뿐이다.
퍼블릭 API 바깥에 있는 중요한 동작들(의존성)은 어떻게 해결해야할까?
- 단위 테스트는 '비교적 격리된 방식'으로 단위 테스트를 하는 것을 목표로 해야한다.
- 하지만, 코드는 다른 것들에 의존하는 경향이 있고, 코드의 모든 동작을 완벽하게 테스트하기 위해 종종 입력을 설정하고 부수효과를 검증해야한다.
- 의존성을 실제로 사용하는 것에 대한 대안으로 테스트더블이 있다.
테스트 더블을 사용해야 하는 이유
- 테스트 단순화
- 일부 의존성은 테스트에 사용하기 까다롭고 힘들다. 의존성은 많은 설정이 필요하거나 하위 의존성을 설정해야할 수 있다.
- 이러면 테스트는 복잡하고 구현 세부사항과 밀접하게 결합 될 수 있다.
- 따라서 의존성을 실제로 사용하는 대신, 테스트 더블을 사용하면 작업이 단순해진다.
- 테스트로부터 외부세계 보호
- 일부 의존성은 실제로 부수효과를 발생한다. 코드의 종속성 중 하나가 실제 서버에 요청을 전송하거나 실제 DB에 값을 쓰게 되면 사용자나 비즈니스에 중요한 프로세스에 나쁜 결과를 초래할 수 있다.
- 이러한 상황에서 테스트 더블을 사용하면 외부 세계에 있는 시스템을 테스트 동작으로부터 보호할 수 있다.
- 외부로부터 테스트 보호
- 외부 세계는 비결정적일 수 있다. 다른 시스템이 데이터베이스에 쓴 값을 의존성 코드가 읽는다면 이 값은 시간이 지남에 따라 변경될 수 있다. 이 경우 테스트 결과를 신뢰하기 어려울 수 있다. (플래키)
- 반면에 테스트 더블은 항상 동일하게 결정적 방식으로 작동하도록 설정 가능하다.