Test-Driven Development 의 약자
테스트 코드를 먼저 만들고 실제 프로덕션 코드는 나중에 만드는 개발 방법
설계를 가장 먼저 하고, 프로덕션 코드 개발 이후 맨 마지막에 테스트 작성
테스트 코드를 가장 먼저 작성하고, 이후에 설계 및 프로덕션 개발 진행
Red, Green, Refactor 라고도 부른다.
실패하는 테스트를 먼저 구현해서 테스트를 실패한다.
이후 테스트를 성공시키는 프로덕션 코드를 작성한다.
마지막에 프로덕션 코드와 테스트 코드를 모두 리팩토링한다.
나중에 테스트 코드를 작성해도 테스트 커버리지는 높일 수 있는데 뭐가 다른걸까?
테스트 코드를 나중에 작성하게 되면, 사람의 특성상 테스트 코드 작성을 잊거나 뒤로 미루는 경우가 발생한다. 결국 프로그램의 안정성을 높일 수 있는 테스트 코드를 작성하지 않을 수도 있다. (기능 배포 여부에는 영향을 미치지 않는 부분이므로)
유의할 점: 테스트 커버리지가 높다고 해서 무조건 좋은 코드인 건 아니다!
=> ROI(Return On Investment, 투자수익률)의 관점에서 테스트 코드는 '비용'이기 때문
오버 엔지니어링을 방지할 수 있다.
테스트 코드를 먼저 작성하면 이때 요구사항에 맞춘 코드를 어느 정도 작성하기 때문에 이후 세부 프로덕션 코드에서 정말 필요한 만큼만 코딩할 수 있다.
오버 엔지니어링
미래에 필요할 것 같은 구현을 지레짐작하여 불필요한 코드를 작성하는 것
설계에 대한 피드백이 빠르다.
테스트 코드 작성 후 바로 설계를 진행하면 설계가 이상할 때 그 과정에서 바로 체크를 할 수 있다.
=> 테스트 코드를 작성할 때 복잡하고 잘못된 설계를 테스트 코드로 구현하려고 하면 새로운 테스트 코드를 작성하는 것 자체가 점점 더 어려워지기 때문이다.
결국 테스트 코드를 먼저 작성하고 개발을 한다는 것은
실제 프로덕션 개발로 넘어가기 전 제대로 된 '설계'를 가지고 시작한다는 말과 동일하다.
만약 TDD가 아닌 일반 개발 방식이라면, 설계 이후에 따로 확인하는 과정 없이 바로 프로덕션 개발로 진행하므로 개발이 모두 끝난 이후 테스트를 할 때 실제 구현 코드의 잘못된 점을 알게 되어 리팩토링 과정이 복잡해진다.
만약 TDD가 설계방법론이라면 TDD를 통해 '좋은 설계'를 얻을 수 있어야 한다. 하지만..
=> 이 내용이 'TDD가 설계방법론이 아니다'라는 걸 증명해주고 있다. (??)
물론 TDD가 설계에 전혀 영향을 미치지 않는다는 건 아니다.
TDD는 강한 결합과 의존성 역전 원칙 위배를 경고하고,
불명확한 설계 지점을 발견해주기도 한다.
의존성 역전 원칙 (Dependency Inversion Principle)
객체는 저수준 모듈보다 고수준 모듈에 의존해야 한다는 원칙
고수준 모듈: 객체의 형태나 추상적 개념 (Interface)
저수준 모듈: 구현된 객체 (Implementation 안의 구현체)
즉, 객체는 객체보다 인터페이스에 의존해야 한다.
(= 가급적 객체의 상속은 인터페이스를 통해 이루어져야 한다)
하지만 TDD에 전적으로 지나치게 설계를 의존하게 된다면
테스트 하기에만 좋은, 결국 안 좋은 설계가 만들어진다.
(이미지 첫 번째 항목 ??)
Implementation 안에 있는 구현체들을 직접 테스트한다.
=> 이렇게 구현체 자체를 리팩토링하게 되면 테스트들이 깨질 수 있다.
(Implementation 안에 있는 구현체들은 서로 결합도를 가질 수 있기 때문에)
구현체가 아닌 설계(Interface)를 테스트한다.
=> 가장 바깥쪽의 설계, 즉 Interface를 테스트하면
Implementation 안의 구현체를 아무리 리팩토링해도 테스트 케이스가 깨지지 않는다.
먼저 프로그램 전반의 테스트 종류에 대해 알아보면 다음과 같다.
프로그램 테스트에는 크게 인수, 부합, 기능, 통합, 단위 테스트가 존재한다.
단위 테스트는 제일 작은 범위인 코드 단위를 테스트하며, 단위 테스트를 제외한 나머지 4개의 테스트들은 가장 기본적인 단위 테스트로부터 점점 더 범위를 넓혀나가며 테스트를 하게 된다.
각 단위가 정확하게 동작하는지 검사하고
문제가 발생 시 어느 부분이 잘못되었는지 빨리 확인할 수 있게 해 준다.
=> 프로그램의 안정성 증가
내부 구현체인 코드를 수정하거나 리팩토링해도 테스트 케이스가 깨지지 않는다. (=> 의존성 역전 원칙을 위배해도 괜찮다 ??)
하나의 단위 테스트가 너무 길어지거나 복잡해지면 프로덕션 코드에서 필히 잘못될 수 있는 부분이라고 본다.
따라서 하나의 테스트 메서드에서 너무 많은 기능을 수행한다고 생각되면 테스트 단계에서 바로 리팩토링을 진행할 수 있다.
일명 '샘플 코드'라고 하며, 예외 상황, 용도, 의존 관계를 한눈에 파악 가능하다. (테스트 코드 = '동작하는 문서')
단위 테스트는 배포되는 코드와 일치하므로, 프로덕션 코드에 맞춰 항상 최신 상태로 유지된다.