•
이 장은 간결하고 효과적으로 텍스트를 작성하는 방법에 대해 알아본다.
읽거나 유지보수하기 쉽게 테스트를 만들어라
•
다른 프로그래머가 수정하거나 새로운 테스트를 더하기 쉽게 테스트 코드는 읽기 쉬워야 한다.
•
테스트 코드를 작성하는 것은 지금 시점에 이르러 선택이 아닌 필수가 되었기 때문에 작성하는 것은 여지가 없다. 즉 작성은 필수니 작성할 땐, 가독성 좋게 작성해야 한다는 것이다.
•
또한 테스트는 특정한 동작 단위로 설계되므로 해당 동작을 설명하는 가장 좋은 예시가 되고는 한다.
테스트를 더 읽기 쉽게 만들기
•
일반적인 설계원리를 따른다면 비즈니스 로직이 아닌 구현 세부 사항은 사용자가 볼 필요가 없으므로 숨겨서 더 중요한 내용이 눈에 잘 띄게 해야한다.
◦
테스트도 그렇다. 테스트의 목적은 메소드에서 드러나는데, 이는 보통 코드 블록보다 추상화 수준이 한, 두 단계 높아야한다.
•
코드 블록 내의 추상화 수준이 뒤죽박죽이라면 가독성을 깨치는 행위다. 코드 블록의 뎁스가 1칸으로 유지하듯이 추상화 수준도 한, 두 단계 내려가는 것을 유지해야 한다.
•
프로덕트 코드를 리팩토링할 때 사용했던 방법인 메소드를 먼저 적어보기도 좋은 방법이다.
읽기 편한 메시지 만들기
•
검증 라이브러리를 사용해서 assert문의 가독성을 그대로 두지 말아라.
•
추출할 필요는 없지만 굳이 가독성이 썩 좋지않은 테스트 프레임워크 내장 assert 대신 더 나은 가독성을 제공하는 라이브러리를 사용하라는 것이다.
좋은 테스트 입력값의 선택
•
테스트에 적절한 입력값을 선택하는 기술도 있다. 좋은 입력값은 코드를 구석구석 철저하게 테스트한다. 하지만 이는 간단해서 읽기도 쉬워야 한다.
◦
가능하면 가장 간단한 입력으로 코드를 완전히 검사할 수 있어야 한다.
•
요구사항이 음수라면 -1234234를 사용하지말고 -1정도만 되어도 충분하다는 것이다.
◦
필요한 작업을 수행하는 범위에서 가장 명확하고 간단한 테스트 값을 선택하라.
•
코드를 구석구석 철저하게 테스트하고자 완벽한 입력을 찾는 것 대신 작은 테스트를 여러 개 사용하라.
테스트 함수에 이름 붙이기
•
테스트 코드는 주로 메소드 단위로 만들어진다. 테스트하려는 메소드나 상황에 메소드를 하나씩 만들게 된다.
•
테스트 함수를 위해 좋은 이름을 지어주어야 한다. 전혀 의미가 없는 이름을 사용하면 곤란하다. 테스트를 상세하게 묘사할 수 있는 이름이 좋다.
•
다음과 같은 항목들이 드러나면 좋다.
◦
테스트되는 클래스
◦
테스트되는 함수
◦
테스트되는 상황이나 버그
테스트 코드를 개선시킬만한 아이디어
•
테스트가 너무 길고 비즈니스 로직이 아닌 구현 세부 사항을 담고 있지는 않은가 생각해보자.
•
새로운 검증문을 추가하기 쉬운지 생각해보자.
•
테스트 실패 메시지가 적절한 정보를 제공하는 지 고민해보자. 디버깅에 도움이 되어야 한다.
•
모든 것을 한 꺼번에 테스트하려고 애쓰는지 생각해보자. 나누면 쉬워진다.
•
테스트 입력이 간단한가에 대해 고민해보자. 내가 제공한 값이 특별한 의미가 있는지, 없다면 간략화하는 것이 좋다.
•
테스트 입력값과 검증문이 코드를 충분히 테스트하는 지 생각해보자.
•
테스트 메소드 명이 적절한지 고민해보자.
테스트에 친숙한 개발
•
테스트하기 용이한 개발은 잘 정의된 인터페이스를 가지고 지나치게 많은 상태나 동작을 포함하지 않으며 감추어야할 데이터를 노출시키지 않는다.
•
테스트하기 용이한 설계는 서로 다른 책임을 가진 부분을 서로 분리된 부분으로 구상하는 전체적으로 봤을때 잘 조직된 코드를 낳는다.
•
테스트하기 힘든 코드의 특징은 다음과 같다.
◦
전역변수를 사용한다. 다른 말로는 공유 의존성을 사용한다.
▪
테스트할 때마다 모든 전역 변수를 초기화해야하기 때문이다. 적절히 초기화가 이루어지지 않을 경우, 테스트가 독립된 상태로 실행되지 않을 가능성이 있다.
◦
코드가 많은 외부 컴포넌트를 사용한다.
▪
외부 의존도가 높은 테스트는 피드백 속도가 느리고 실패할 가능성을 가늠할 수 없다.
◦
코드가 비결정적인 행동을 가진다.
▪
프로그램이 경합 조건이나 재현하기 어려운 버그를 가지고 있을 확률이 높다.
•
테스트하기 좋은 코드의 특징은 다음과 같다.
◦
클래스들이 내부 상태를 적게 가지고 있다.
▪
캡슐화된 멤버가 적으므로 테스트 작성이 수월하고 설계상 이해하기도 쉽다.
◦
클래스/함수가 한 번에 하나의 일만 수행한다.
◦
클래스가 다른 클래스에 의존하지 않고 서로 상당히 떨어져있다.
▪
각 클래스가 독립적으로 테스트되며 병렬적으로 개발이 가능하다.
◦
함수들이 간단하고 잘 정의된 인터페이스를 가지고 있다.
지나친 테스트
•
테스트에 집중한다고 좋은 것이 아니다. 뭐든 도가 지나치면 화를 낳는 법이다. 다음은 몇 가지 예다.
◦
테스트를 가능하게 하려고 제품 코드의 가독성을 희생시키는 경우, 테스트 코드를 작성하는 것은 윈윈이 되어야 한다.
◦
100% 코드 테스트에 집착하는 일, 모든 코드를 테스트하려하지 말자. 가성비가 좋지 않은 행위다.
▪
버그가 야기하는 비용이 어느 정도인가에 따라 테스트 비용이 결정된다.
◦
테스트 코드로 실제 제품 개발에 차질을 빚게되는 경우, 테스트 코드를 작성하는 것은 프로젝트의 일부다. 프로젝트보다 우선시 되어서는 안된다.
요약
•
테스트를 개선하기 위한 구체적인 항목은 다음과 같다.
◦
각 테스트의 최상위수준은 간결해야 한다. 이상적으로는 각 테스트의 입출력이 한 줄의 코드로 설명될 수 있어야 한다.
◦
테스트에 실패하면 버그를 추적해서 디버깅에 도움이 되는 에러 메시지를 출력해야 한다.
◦
코드의 구석구석을 철저하게 실행하는 가장 간단한 입력을 사용하라.
◦
무엇이 테스트되는지 분명하게 드러나도록 테스트 함수에 충분한 설명이 포함된 입력을 부여하라.
◦
무엇보다도 테스트의 수정이나 추가가 쉬워야 한다.