Search
Duplicate

9장 단위 테스트

개인적 감상평

나를 포함한 많은 개발자들은 어째서 단위테스트를 해야하는지에 대해 공감하지도 못하고, 테스트가 필요하더라도 그냥 구현을 한 뒤 몇 번 돌려보고 판단한다.
하지만, 이번 장을 통해 단위 테스트가 왜 필요한지를 알게된다.
빨리 기능 구현해서 일정맞춰야 하는데 무슨 테스트야!? 하는 내게 똑바로 가는게 제일 빨리 가는 것이다. 라는 것을 알려준다.
테스트 케이스 코드가 없는 프로젝트는 유지보수, 인수인계, 변경에 취약 할 수밖에 없다.
기능 변경이 잦을수록 내가 놓치는 부분이 생기고 그로인해 문제있는 코드가 배포되는 순간 말 그대로 대형사고가 터진다.

1. TDD 법칙 세 가지

실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

2. 깨끗한 테스트 코드 유지하기

더러운 테스트 코드는 없느니만 못하다.
테스트 코드가 복잡할수록 테스트 케이스를 작성하는 시간이 더 오래 걸린다.
기능이 추가되거나 변경될 때마다 테스트 케이스를 유지/보수 하는 비용은 늘어난다.
테스트 코드는 실제 코드 못지 않게 깨끗하게 짜야 한다.

3. 테스트는 유연성, 유지보수성, 재사용성을 제공한다.

테스트 케이스가 있고 커버리지가 높을 수록 유연한 기능 추가/변경이 쉽다.
아키텍처나 설계가 부실한 곳에서 더 효과있다.
기능 변경이 되더라도 테스트 케이스만 다 통과한다면 문제가 발생하지 않기 때문

4. 깨끗한 테스트 코드

가독성, 가독성, 가독성
깨끗한 테스트 코드를 만들기 위해서는 가독성이 중요하다.
중복된 코드를 합치고, 테스트 케이스에 노출이 필요없는 코드는 숨긴다.
BUILD-OPERATE-CHECK¹ 패턴이 이런 테스트 구조에 적합하다.
Build: 테스트 전 필요한 데이터를 빌드하고,
Operate: 테스트할 API 메소드를 호출하고
Check: 실행한 메소드의 결과가 시스템에 준영향과 DB에 준 영향을 check(확인) 하라.
핵심은 잡다하고 세세한 코드를 다 없애 정말 필요한 자료 유형과 함수만 사용한다는 점.

5. 도메인에 특화된 테스트 언어

테스트 코드를 작성하며 세세한 정보와 함수를 은닉하고 테스트에 필요한 함수만 노출시키는데 이렇게 특수 API가되며 이게 테스트 언어다.
깨끗한 테스트 코드를 만들기 위해 리팩토링을 하며 더 간결하고 표현력 있는 코드를 만들어라.

6. 이중 표준

테스트 API코드에 적용하는 표준은 실제 코드에 적용하는 표준과 다르다.
실제 코드만큼 퍼포먼스를 따지지 않아도 된다.
테스트 환경에서 돌아가는 코드이기 때문에 요구사항이 다르기 때문이다.

7. 테스트당 assert 하나

최대한 하나의 테스트에서는 하나의 assert만 할 수 있도록 한다.
하지만, 테스트를 억지로 분리하며 중복되는 코드가 많아질 경우 합치는 것도 좋다.
assert 문을 최대한 줄이는 것에 초점을 맞춘다.

8. 테스트당 개념 하나.

코드의 중복을 고려해 assert 문을 하나로 못줄이는 경우가 많다면 생각을 바꾼다.
테스트 함수에 하나의 개념만 테스트하라.
/** * addMonths ( ) 메서드를 테스트히는 장황한 코드 */ public void testAddMonths() { SerialDate dl = SerialDate.createlnstance(31, 5, 2004); SerialDate d2 = SerialDate.addMonths(1, dl); assertEquals(30, d2.getDayDfMonth()); assertEquals(6, d2.getMonth()); assertEquals(2004, d2.getYYYY()); SerialDate d3 = SerialDate.addMonths(2, d1); assertEquals(31, d3.getDayOfMonth()); assertEquals(7, d3.getMonth()); assertEquals(2004, d3.getYYYY()); SerialDate d4 = SerialDate.addMonths(1, SerialDate.addMonths(1, d1)); assertEquals(30, d4.getDayOfMonth()); assertEquals(7, d4.getMonth()) ; assertEquals(2004, d4.getYYYY()); }
Java
복사
⇒ 위 코드를 보면 개행순으로 3가지의 테스트를 하는데, 얼핏보면 다 날짜를 검사하는 것 같아서 같은 개념을 테스트하는거니 괜찮을 것 같지만, 사실 다 다른개념으로
1.
첫 케이스는 30일로 끝나는 한 달을 더하면 날짜는 30일이 되어야지 31이 되면 안됩니다.
2.
두 번째 케이스는 2달을 더하면 그리고 그 달이 31일로 끝나면 31일이 되어야 합니다.
3.
마지막은 31일로 끝나는 한 달을 더하면 30일이 되어야지 31일이 되면 안됩니다.
각각의 다른 개념을 가지고 있기에 함수를 나누는게 맞으며, 2월같은 마지막 날짜가 28일인 경우의 테스트 케이스도 채워야 합니다.

9. F.I.R.S.T

깨끗한 테스트가 되기 위해서는 다음 다섯 가지 규칙을 따릅니다.
빠르게(First) : 테스트는 빨라야 합니다. 테스트가 느리면 자주 돌리지 못하고 그 시간만큼 코드를 정리하지 못 할뿐 아니라 테스트를 자주 수행할 수 없습니다.
독립적으로(Independent)
: 각각의 테스트가 서로 의존하면 안됩니다. 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안됩니다.
반복가능하게(Repeatable)
: 테스트는 어떤 환경에서든 반복 가능해야 합니다.
실제 환경, QA환경, 오프라인 환경 모두 가능해야 합니다. 테스트가 돌아가지 않는 환경이 있다면 핑계꺼리가 되며 테스트를 수행하지 못해도 넘겨야하는 상황이 생깁니다.
자가검증하는(Self-Validating)
: 테스트는 Boolean 값으로 결과를 내야 합니다. (성공 or 실패)
통과했는지를 알기 위해 로그 파일이나 콘솔창을 봐야하는것은 잘 못된 것입니다.
적시에(Timely)
: 테스트는 적시에 작성해야 합니다.
즉, 테스트하려는 실제 코드를 구현하기 직전에 구현합니다.
실제 코드를 작성 한 뒤에 테스트 코드를 작성하려 하면 테스트하기가 어려울 수도 있고, 테스트가 불가능하도록 실제 코드를 설계할 수도 있다.