- 오직 자동화된 테스트가 실패할 경우에만 새로운 코드를 작성한다.
- 중복을 제거한다.
- 빨강 - 실패하는 작은 테스트를 작성한다. 처음에는 컴파일 조차 되지 않을 수 있다.
- 초록 - 빨리 테스트가 통과하게끔 만든다. 이를 위해 어떤 죄악을 저질러도 좋다.
- 리팩토링 - 일단 테스트를 통과하게만 하는 와중에 생겨난 모든 중복을 제거한다.
- 2장. 타락한 객체
- 느낌(부작용에 대한 혐오감)을 테스트로 변환하는 것은 TDD 의 일반적인 주제다.
- 5장. 솔직히 말하자면
$5 + 10CHF = $10
를 위한 테스트를 작성하는게 너무 큰 단계임으로 단계를 쪼개서 Fran 이라는 클래스 생성하고5CHF x 2 = 10CHF
를 테스트 했다.- 테스트 작성, 컴파일되게 하기, 실패하는지 확인하기 위해 실행, 실행되도록 만듬, 중복제거의 단계에서 처음의 네단계를 빨리 진행해야 한다.
- 중복제거의 단계에서 Money 라는 상위개념을 도입했다.
- 6장. 돌아온 '모두를 위한 평등'
- 공통된 코드를 상위 클래스인 Money 로 단계적으로 옮겼다. (차이점 일치시키기 리팩터링)
- 7장. 사과와 오렌지
- 8장. 객체 만들기
- 하위 클래스를 제거하기 위해 하위클래스에 대한 직접적인 참조를 없앴다.
- 팩터리 메소드를 사용해서 하위 클래스 생성자에 대한 직접적인 참조를 없앴다.(팩토리 메소드 패턴)
- Dollar 와 Franc 의 time 메서드의 시그니처를 통일 시켜서 중복 제거를 향해 한단계 나아갔다.(차이점 일치시키기 리팩터링)
- 하위 클래스를 제거하기 위해 하위클래스에 대한 직접적인 참조를 없앴다.
- 9장. 우리가 사는 시간
- currency 개념을 도입했다.
- 처음에는 각각의 클래스에서 currency 로 서로 다른 문자열을 되돌려주는 메소드를 구현했지만, Dollar 와 Franc 두 클래스를 모두 포함할 수 있는 동일한 구현을 원하기 때문에 currency 를 인스턴스 변수로 만들고 생성자에서 이를 설정하도록 했다. 그 결과 Dollar 와 Franc 의 currency 가 동일해져서 Money 로 위치를 옮길 수 있게 되었다.
- Dollar 와 Franc 의 생정자에 currency 파라미터를 추가해서 구현을 동일 하게 만든 뒤(다른 부분들을 팩토리 메서드로 옮김으로써 두 생성자를 일치시켰다.) 마찬가지로 Money 로 위치를 옮겼다.
- 10장. 흥미로운 시간
- times 의 구현을 일치시키려고 했지만 마땅한 방법이 없어서 팩토리 메서드를 인라인 시켰다. (두 times()를 일치시키기 위해 그 메서드들이 호출하는 다른 메서드들을 인라인시킨 후 상수를 변수로 바꾸어 주었다.)
- times 에서 Money 생성자를 사용하도록 했다. 그 결과 Money 는 abstract 가 아니라 concrete 클래스가 되었다.
- Dollar 와 Franc 의 times 의 구현이 동일하게 되었지만 테스트를 통과하지 못했다.
- times 의 구현을 테스트가 통과하는 상태로 되돌린 다음 testDifferentClassEquality 를 정의했다. 타입이 달라도 동등할 수 있다.
- 테스트가 통과하게 한다음 동일한 times 의 구현을 Money 로 옮겼다.
- 11장. 모든 악의 근원
- 하위 클래스의 속을 들어내는 걸 완료하고, 하위 클래스를 삭제했다.
- 기존 소스 구조에서는 필요했지만 새로운 구조에서는 필요 없게 된 테스트를 제거했다.
- 12장. 드디어, 더하기
- 큰 테스트를 작은 테스트로 줄여서 발전을 나타낼 수 있도록 했다.
- 다중 통화 사용에 대한 내용을 시스템의 나머지 부분에게 숨겨야 하지만 지금의 설계로는 그럴 수 없다. 가지고 있는 객체가 우리가 원하는 방식으로 동작하지 않을 경우엔 그 객체와 외부 프로토콜이 같으면서 내부 구현은 다른 새로운 객체(imposter)를 만들 수 있다. Money 와 비슷하게 동작하지만 사실은 Money 사이의 계산 수식을 의미하는 Expression 메타포를 도입했다.
- Expression 에 reduce 오퍼레이션을 위치시킬 수도 있었지만, 이렇게 되면 이후 Expression 과 관련된 수많은 오퍼레이션이 Expression 에 위치하게 된다. 따라서 다중 통화 사용을 가능하게 하는 핵심적인 메타포인 Expression 는 수식의 표현 책임만 남기고 reduce 오퍼레이션을 bank 에 위치시켰다.
- 13장. 진짜로 만들기
- Expression 은 계산 수식에 대한 메타포 이다. reduce 를 진짜로 구현하기 위해 Sum 의 도입을 강요하기 위한 테스트를 작성했다.
- 테스트를 통화하게 하기 위해서 instance of 와 같은 죄악을 저질렀다. 테스트가 통과한 후에 시그니쳐를 일치시킨 후, interface 로 오퍼레이션을 옮기고, 각각의 클래스에서 구현하는 리팩토링을 진행했다. (다형성을 사용해서 명시적인 클래스 검사를 제거했다.)
- 14장. 바꾸기
- reduce 오퍼레이션에 필요할 것으로 보이는 인자 Bank 를 추가했다.
- 코드와 테스트 사이에 있는 하드코딩 되어있는 환율 데이터 중복을 제거했다.
- 리팩터링을 하다가 실수를 했고, 그 문제를 분리하기 위해 또하나의 테스트를 작성하면서 전진했다.
- $5 + $5 = $10 기능을 구현함에 있어서 발생한 모든 중복을 해결했다.
- 15장. 서로 다른 통화 더하기
- 최종적으로 우리가 원하는 인터페이스로 테스트 코드를 작성했다. 우리가 원하는 인터페이스는 reduce 전까지는 Expression 을 사용한다.
- 단계적으로 코드를 수정하기 위해서 일단 테스트를 원래대로 옮긴 후 각각의 클래스의 plus 오퍼레이션이 Expression 을 사용하도록 했다.
- 테스트 코드를 다시 우리가 원하는 인터페이스로 변경한 후 컴파일러의 지시를 따라 수정했다. plus 오퍼레이션을 Expression 으로 올렸다.
- 16장. 드디어, 추상화
- Expression.times 를 구현하기 위해 일단 testSumTimes 를 구현했다.
- 실험을 위한 테스트를 작성하고 제대로 되지 않아서 버렸다.
- 17장. Money 회고
- 다음에 할 일은 무엇인가
- 어떤 테스트들이 추가로 더 필요할까? 를 질문한다.
- 할일 목록이 빌 때가 그때가지 설계한 것을 검토하기에 적절한 시기다. (말과 개념이 잘 통하는지, 현재의 설계로는 제거하기 힘든 중복이 있는지 등.)
- 메타포
- 메타포는 중요하다 어떤 메타포를 사용하는가에 따라서 설계가 완전히 다른 방향으로 흘러간다.
- JUnit 사용도
- 코드 메트릭스
- ...
- cyclomatic complexity: 명시적인 흐름 제어 대신 사형성을 주로 사용했기 때문에 실제 코드의 복잡도 역시 낮다.
- 프로세스
- TDD 의 주기는 다음과 같다.
- 작은 테스트를 추가한다.
- 모든 테스트를 실행하고, 실패하는 것을 확인한다.
- 코드에 변화를 준다.
- 모든 테스트를 실행하고, 성공하는 것을 확인한다.
- 중복을 제거하기 위해 리팩토링한다.
- TDD 의 주기는 다음과 같다.
- 테스트의 질
- 조건문이 메세징으로 변경하는등 모든 입력의 경우를 따져서 테스트를 작성해여 테스트 커버리지를 높이는 대신에, 프로그램의 로직을 단순화 하여 동일한 테스트가 다양한 경우를 다루게하는것으로 테스트 커버리지를 높일 수 있다.
- 최종 검토
- TDD 를 가르칠 때 사람들이 자주놀라는 세가지는 다음과 같다.
- 테스트를 확실히 돌아가게 만드는 세가지 접근법: 가짜로 구현하기, 삼각층량, 명백하게 구현하기.
- 설계를 주도하기 위한 방법으로 테스트 코드와 실제 코드사이의 중복을 제거하기.
- 테스트 사이의 간격을 조절할 수 있는 능력.
- TDD 를 가르칠 때 사람들이 자주놀라는 세가지는 다음과 같다.
- 다음에 할 일은 무엇인가
테스트 주도 개발에서의 테스트트 자동화된 테스트를 의미한다.
각각의 테스트는 서로 독립적이어야 한다.
테스팅해야하는 것에 대한 목록을 유지한다. 추가적으로 떠오르는 것을 목록에 추가함으로써 집중력이 분산되는걸 막는다. 테스트는 한번에 하나씩 구현해 간다.
실제 코드를 작성하기전에 테스트를 먼저 작성한다음 실제 코드를 작성한다.
테스트를 작성할 때 단언을 가장 먼저 작성하라. 시스템에 대해서 어떤 것이 성취되어야한 다는 진술로 시스템 개발을 시작한다. 프로그램으로 하여금 어떤 것이 성취되어야 한다는 것을 테스트로 나타낸 다음 프로그램을 작성을 시작한다. 테스트로 하여금 어떤 것이 성취되어야 한다는 것을 단언하는 것으로 테스트를 작성한다.
테스트를 읽을 때 쉽고 따라가기 좋은 데이터를 사용해야 한다. 목적 따라서는 실제 세상에서 얻어진 실제 데이터를 사용해야한다.
코드는 결국 나중에 사람이 읽는다. 코드의 의도를 보여줄 수 있는 데이터를 사용해야한다. 예상되는 값과 실제 값을 포함하고 이 둘의 관계를 들어낼 수 있게 노력해야 한다.