[Book] TDD, 켄트 벡 1부 화폐예제

Hyunta·2021년 8월 3일
0

테스트 주도 개발

테스트 주도 개발의 궁극적인 목표: 작동하는 깔끔한 코드

근거:
  1. 예측 가능한 개발 방법. 끊임없이 발생할 버그에 대해 걱정하지 않고, 일이 언제 마무리 될지 알 수 있다.
  2. 코드가 가르쳐주는 모든 교훈을 학습할 기회를 갖게 된다. 처음 생각나는 대로 후딱 완료해 버리면 두 번째 것, 더 나은 것 에 대해 생각할 기회를 읽게 만든다.
  3. 당신이 만든 소프트웨어는 사용자의 삶을 향상시켜 준다.
  4. 동료들이 당신을 존경할 수 있게 해주며, 당신 또한 동료들을 존경할 수 있게 된다.
  5. 작성하는 동안 기분이 좋다.
규칙:
  1. 오직 자동화된 테스트가 실패할 경우에만 새로운 코드를 작성한다.
  2. 중복을 제거한다.
책을 읽고나면:
  1. 단순하게 시작하고
  2. 자동화된 테스트를 만들고
  3. 새로운 설계 결정을 한 번에 하나씩 도입하기 위해 리팩토링을 할 준비가 될 것이다.

1부. 화폐 예제

소스코드 링크

TDD의 리듬

  1. 재빨리 테스트를 하나 추가한다.
  2. 모든 테스트를 실행하고 새로 추가한 것이 실패하는지 확인한다.
  3. 코드를 조금 바꾼다.
  4. 모든 테스트를 실행하고 전부 성공하는지 확인한다.
  5. 리팩토링을 통해 중복을 제거한다.

놀랄 포인트

  • 각각의 테스트가 기능의 작은 증가분을 어떻게 커버하는지
  • 새 테스트를 돌아가게 하기 위해 얼마나 작고 못생긴 변화가 가능한지
  • 얼마나 자주 테스트를 실행하는지
  • 얼마나 수 없이 작은 단계를 통해 리팩토링이 되어가는지

1장. 다중 통화를 지원하는 Money 객체

Money Class

종목가격합계
IBM10002525000
GE40010040000
합계65000

다중 통화를 지원하는 Money Class

종목가격합계
IBM100025USD25000USD
GE400150CHF60000CHF
합계65000USD

환율 Class

기준변환환율
CHFUSD1.5

필요한 테스트

  • 통화가 다른 두 금액을 더해서 주어진 환율에 맞게 변한 금액을 결과로 얻을 수 있어야 한다.
  • 어떤 금액(주가)을 어떤 수(주식의 수)에 곱한 금액을 결과로 얻을 수 있어야 한다.
할 일

$5 + 10CHF = $10 (환율이 2:1일 경우)
$5  x 2  = $10
amount를 private으로 만들기
Dollar 부작용?
Money 반올림?

Money Class 와 Test 생성

  1. Money Class

  1. MoneyTest


수행한 작업

  • 우리가 알고 있는 작업해야 할 테스트 목록을 만들었다.
  • 오퍼레이션이 외부에서 어떻게 보이길 원하는지 말해주는 이야기를 코드로 표현했다.
  • JUnit에 대한 상세한 사항들을 잠시 무시
  • 스텁 구현을 통해 테스트를 컴파일
  • 돌아가는 코드에서 상수를 변수로 변경하여 점진적으로 일반화
  • 새로운 할일을 처리하지 않고 항 일 목록에 추가하고 넘어갔다.

2장. 타락한 객체

일반적인 TDD 주기

  1. 테스트 작성. 이야기 만들기. 원하는 인터페이스를 개발. 올바른 답을 얻기 위해 필요한 이야기의 모든 요소를 포함
  2. 실행 가능하게 만든다. 빨리 초록막대 보기. 깔끔하고 단순한게 명백히 보이면 수행, 아니라면 일단 적어놓고 문제 해결.
  3. 올바르게 만든다. 죄악들을 수숩. 중복을 제거하고 초록막대로 되돌리기.
작동하고 깔끔한 코드 중 작동부분을 먼저 해결
할 일

$5 + 10CHF = $10 (환율이 2:1일 경우)
~~$5  x 2  = $10~~
amount를 private으로 만들기
Dollar 부작용?
Money 반올림?

  • 가짜로 구현하기: 상수를 반환하게 만들고 진짜 코드를 얻을 때까지 단계적으로 상수를 변수로 바꾸어 간다.
  • 명백한 구현 사용하기: 실제 구현을 입력한다.

3장. 모두를 위한 평등

할 일

$5 + 10CHF = $10 (환율이 2:1일 경우)
~~$5  x 2  = $10~~
amount를 private으로 만들기
~~Dollar 부작용?~~
Money 반올림?
**equals()**
hashCode()
Equal null
Equal object

초록막대를 보기 위한 전략 세번째:

  • 삼각측량: 예제가 두개 이상있을 때, 비교를 통해 구성

4장. 프라이버시

임시 변수인 product를 없애면, 일련의 오퍼레이션이 아니라 참인 명제에 대한 단언들이므로 우리의 의도를 더 명확하게 이야기해준다.

Dollar의 amount 인스턴스 변수를 사용하는 코드는 Dollar 자신밖에 없게 됐다. 따라서 변수를 private으로 변경할 수 있다.

할 일

$5 + 10CHF = $10 (환율이 2:1일 경우)
$5 x 2 = $10
amount를 private으로 만들기
Dollar 부작용?
Money 반올림?
equals()
hashCode()
Equal null
Equal object


5장. Franc-ly Speaking

할 일

$5 + 10CHF = $10 (환율이 2:1일 경우)
$5 x 2 = $10
amount를 private으로 만들기
Dollar 부작용?
Money 반올림?
equals()
hashCode()
Equal null
Equal object

5CHF x 2 = 10CHF

$5 + 10CHF = $10 (환율이 2:1일 경우)에 접근하기 위해서 우선 Franc 클래스를 생성한다. Dollar 클래스를 복사해서 Franc 클래스를 만들면 해결.


6장. 돌아온 '모두를 위한 평등'

공용 equals

Dollar 와 Franc은 공통 기능을 갖는다. equals 기능을 상위클래스를 이용해 refactoring 하자.

  • 공통된 코드를 Dollar에서 상위 클래스인 Money로 옮겼다.
  • Franc도 Money의 하위클래스로 만들었다.
  • 불필요한 구현을 제거하기 전에 두 eqauls()구현을 일치시켰다.

7장. 사과와 오렌지

Franc과 Dollar 비교하기

문제점: assertFalse(new Franc(5).equals(new Dollar(5)));가 실패한다.

Dollar과 Franc의 차이점을 만들어야 한다.

&& getClass().equals(money.getClass());

equals에 클래스도 비교하는 조건을 추가한다.

  • 화폐단위를 추가할 수 있지만, 동기가 모자라므로 아직 설계하지 않는다.

8장. 객체 만들기

Dollar/Franc 중복

두 times() 구현이 똑같다

class Money{
	static Dollar dollar(int amount){
    	return new Dollar(amount);
	}
    
    static Franc franc(int amount){
        return new Franc(amount);
    }
}    
  • 동일한 times()의 두 변이형 메서드 서명부(Dollar, Franc -> Money)를 통일시켜 중복제거에 가까워짐
  • 최소한 메서드 선언부만이라도 공통 상위 클래스로 옮겼다.

9장. 우리가 사는 시간

통화?

테스트 단위가 커서 오류가 생겼을 경우 줄일 수 있어야 한다.

class Money{
	static Money dollar(int amount){
    	return new Dollar(amount, "USD");
	}
}    
class Dollar{
    private String currency;
    
    Dollar(int amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }
   
    Money times(int multiplier) {
        return Money.dollar(amount * multiplier);
    }
    
}
  • 큰 설계 아이디어를 다루다가 문제가 생기면 작게 만들어라.
  • 다른 부분들을 호출자로 옮김으로써 두 생성자를 일치시킴
  • times()가 팩토리 메서드를 사용하도록 만들기 위해 리팩토링 중단
  • 동일한 생성자들을 상위클래스로 옮김

10장. 흥미로운 시간

공용 times

Franc 과 Dollar에 구현되어있던 times() 메서드를 상위클래스로 옮기기 위해 생성자를 Money로 변경

eqauls에서 Class를 비교하지 않고 이제 currency를 비교하도록 설정.


11장. 모든 악의 근원

Dollar/Franc 중복

이제 Dollar, Franc 클래스는 생성자만 가지므로 삭제가능하다.

Money 클래스만 남기고 나머지를 삭제한다.

  • 하위 클래스의 속을 들어내는 걸 완료, 하위 클래스 삭제
  • 새로운 소스 구조에서 필요 없게된 테스트 삭제

12장. 드디어, 더하기

$5+$5 = $10

간단한 덧셈을 먼저 구현하기에 앞서, 계산에 필요한 비유를 신중히 고민.

테스트를 구현하기 위해 클래스를 생성하여 복잡하지만 직관적으로 해결


13장. 진짜로 만들기

$5+$5 = $10
  • 모든 중복이 제거되기 전까지는 테스트를 통과한 것으로 치지 않는다
  • Sum Class를 생성하기 위한 테스트를 작성하였다.
  • 명시적인 클래스 테스트를 제거하기 위해 다형성 사용

14장. 바꾸기

Money에 대한 통화 변환을 수행하는 Reduce
  • 코드와 테스트 사이에 있는 데이터 중복을 끄집어냈다.
  • 별도의 테스트 없이 private helper class를 만들었다.

15장. 서로 다른 통화 더하기

$5 + 10CHF = $10 (환율이 2:1일 경우)
  • 원하는 테스트를 작성하고, 한단계에 달성할 수 있도록 뒤로 물렀다.
  • Expression fiveBuck로 변경 후, 그 영향을 받은 다른 부분들을 변경하기 위해 컴파일러의 지시를 따랐다.

16장. 드디어, 추상화

Expression.plus를 마치려면 Sum.plus를 구현해야하고 Expression.times()를 구현하면 완성된다.


17장. Money 회고

프로세스:

  • 작은 테스트를 추가한다.
  • 모든 테스트를 실행하고, 실패하는 것을 확인한다.
  • 코드에 변화를 준다.
  • 모든 테스트를 실행하고, 성공하는 것을 확인한다.
  • 중복을 제거하기 위해 리팩토링한다.
profile
세상을 아름답게!

0개의 댓글