1부 화폐 예제

TDD의 기본원리

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

chapter 1

이 책은 예제를 통해 개념을 설명해가고 있다.
첫 번째 예제는 다음과 같다.

계속 강조했듯이, 우선 테스트를 만든다. (여기선 Junit을 사용)

public void testMultiplication() {
    Dollar five = new Dollar(5);
    five.times(2);
    assertEquals(10, five.amount);
}

얼마나 테스트를 먼저 만드냐면, 위 코드는 컴파일이 되지 않는데, 이유는 아래와 같다. (이게 코드냐! 싶다)

  1. Dollar 클래스가 없음
  2. 생성자가 없음
  3. times 함수 없음
  4. amount 필드 없음

TDD에서는 우선 위처럼 테스트를 만들고 아래의 문제점들을 찾아 하나씩 해결하는 방식이다.
여기서 위 문제들을 해결해서 컴파일이 되게 만든다. 딱히 예제가 필요하지 않을 것 같다.
컴파일이 되게 만들었다면, 테스트를 통과하게 만들어야 한다.
처음엔 테스트를 빠르게 통과하게 만드는데, 어느정도냐면 임의의 값에 대한 테스트 성공이 아니라, 특정 테스트 값에 대해 성공하도록 수정해도 된다1.

multiple(5, 2)
def multiple (int a, int b):
    return 10

의존성과 중복

목표는 코드를 바꾸지 않으면서 의미있는 테스트를 추가하는 것인데, 현재 이것은 불가능하다.
TDD에서 중요한 것은 코드의 중복을 제거함으로써 의존성을 줄이고 하나의 코드 수정으로 적용될 수 있도록 하는 것이다.

chater 2

TDD의 구체적 기본원리

TDD 기본원리를 조금더 확인해 보면

  1. 재빨리 필요한 모든 요소를 포함한 테스트를 하나 추가한다.
  2. 실행 가능하게 만든다. 깔끔하고 단순한 해법이 명백하다면 그 것을 사용하고, 그렇지 않다면 이전에 했던 것처럼 최대한 빨리 테스트를 통과하게 한다. 여기서 핵심은 테스트를 통과만 할 수 있는 더러운 코드를 잠시는 작성해도 된다는 것이다.
  3. 올바르게 만든다. 위에서 저지른 죄를 되돌리고 중복을 제거한다. 해야할 것들을 리스트 업한다.

TDD vs ADD

코딩의 방향은 작동하는 깔끔한 코드를 얻는 것이다.
Test-Driven Development (TDD):
작동하는 코드를 먼저 만들고 깔끔한 코드로 수정하는 방식.
Architecture-Driven Development (ADD):
깔끔한 코드를 먼저 만들고 작동하는 코드를 만드는 방식.

오류 제어하기

설계상의 결함이나 부작용, 코드상 꺼림찍한 느낌이 있다면 이를 테스트로 변환하고 테스트를 수정하는 것이 TDD의 일반적인 주제이다.
부족한 부분을 보완하기 위해 테스트를 수정하고 이후에 다시 코드를 수정할 수 있도록 한다.

chapter 3

value object pattern

값 객체 패턴이라고 번역되는 이 패턴의 가장 큰 특징은 객체의 인스턴스 변수가 생성자를 통해 설정된 후에는 결코 변하지 않는다는 것이다.
값 객체는 별칭 문제에 대해 걱정할 필요가 없다는 장점이 있다.

하나의 수표에 $5를 설정하고, 다른 수표에서 $5를 설정했을 때, 두 번째 수표의 값을 수정했을 때 첫 번째 수표의 값이 변하게 되는 문제를 별칭 문제라고 한다.

값 객체에서는 위와 같은 상황에서 누군가 $7를 원한다면 새로운 객체를 만들어야 한다.
또 값 객체는 모든 연산에 대해 새 객체를 반환해야 하며, 따라서 equals()를 구현하여 반환된 값들을 판별해야 한다.

Dollar라는 객체에 대해 assertTrue(new Dollar(5).equals(new Dollar(5)))를 만족할 수 있도록 equals()의 구현이 필요하다.
TDD 기본원리에서 배운 무작정 통과를 사용하면 다음과 같이 테스트를 통과할 수 있을 것이다. public boolean equals(Object object) { return true;}

삼각측량

라디오 신호를 수신국 A, B가 감지하고 있을 때, 각 수신국 사이의 거리를 알고 있고 신호의 방향을 알고 있다면 이를 통해 신호의 거리와 방위를 계산하는 계산법이다.
코드에 적용하면 두 개 이상의 예제로 코드를 일반화 하는 방법이다.
어떻게 리팩토링을 해야하는지 감이 오지 않을 때, 설계를 어떻게 해야할지 떠오르지 않을 때 삼각측량을 통해 문제를 다른 방향에서 생각해볼 수 있다.

삼각측량 적용

아래와 같이 테스트를 추가하여 삼각측량을 할 수 있을 것이다.
그러면 return true로 해결할 수 없고, 실제 값을 비교하도록 하는 수정이 비로소 이루어진다.

public void testEquality() {
    assertTrue(new Dollar(5).equals(new Dollar(5)));
    assertFalse(new Dollar(5).equals(new Dollar(6)));
}

public boolean equals(Object object) {
    Dollar = dollar = (Dollar) object;
    return amount == dollar.amount;
}

위 코드는 Equals(null)을 고려하지 않았지만, 괜찮다. 리스트 업하고 나중에 확인하자.

디자인 패턴 적용 과정

  1. 디자인 패턴(값 객체)가 하나의 또 다른 오퍼레이션을 암시한다는 것을 보았다.
  2. 해당 오퍼레이션을 테스트했다.
  3. 오퍼레이션을 간단히 구현하였다.
  4. 테스트를 추가하였다. (삼각측량)
  5. 리팩토링했다.

chapter 4

수정한 코드를 통해 테스트를 수정할 수 있다.
chapter 1의 코드를 수정해본다. Dollar.equals()가 구현되었고, times()는 값 객체 패턴에 따라 새로운 값을 return하게 되었으므로 아래와 같이 테스트와 코드의 결합도를 낮추는 방향으로 수정될 수 있다.

public void testMultiplication() {
    Dollar five = new Dollar(5);
    assertEquals(new Dollar(10), five.times(2));
}

수정 사항은 보다 명확하게 의도를 알 수 있으며, Dollar.amount를 사용하지 않아 변수를 private으로 변경할 수 있게 되었다.

TDD의 생각

완벽함을 위해 노력하지 않는다. 모든 것을 두 번 말함으로써(code와 test로) 자신감을 가지고 결함을 낮추기를 희망한다. 때로 결함이 생길 수 있지만, 그럴 땐 테스트를 어떻게 작성해야 했는지에 대한 교훈을 얻고 다시 시작한다.

여태까지

  • 오직 테스트를 향상시키기 위해서만 개발된 기능을 사용했다.
  • 두 테스트가 동시에 실패하면 망한다는 점을 인식했다.
  • 위험 요소가 있음에도 계속 진행했다.
  • 테스트와 코드 사이의 결합도를 낮추기 위해, 테스트하는 객체의 새 기능을 사용했다.

chapter 5

골치아픈 큰 요구사항과 테스트

$5 + 10CHF = $10 (환율이 2:1 일 경우) 위 요구사항의 테스트에 대해 어떻게 접근할 것인가?
우선 Dollar 테스트를 복사한 후 수정하여 사용해보도록 한다.

public void testFrancMultiplication() {
    Franc five = new Franc(5);
    assertEquals(new Franc(10), five.times(2));
    assertEquals(new Franc(15), five.times(3));
}

위 테스트를 통과하기 위해 어떻게 해야할까?
한 번에 통과하기는 어려울 것이다.
빠르고 쉽게 통과하기 위해, (TDD의 정신이다)
다시 Dollar 코드를 복사하여 Franc로 바꾸는 방법을 사용하도록 한다2.
더럽고 지저분하고 마음이 찝찝하다. 이런 코드를 짠다니 싶지만, TDD 구체적 기본원리를 확인한다. 우선 실행이 되고 테스트를 통과하는 코드를 만들고, 중복 제거와 같은 더러운 코드를 없애는 일은 그 다음임을 기억한다.

검토해보면

  • 큰 테스트를 공략할 수 없다. 그래서 진전을 나타낼 수 있는 자그마한 테스트를 만들었다.
  • 뻔뻔스럽게도 중복을 만들고 조금 고쳐 테스트를 작성했다.
  • 설상가상으로 모델 코드까지 복사하고 수정하여 테스트를 통과했다.
  • 중복이 사라지기 전에는 집에 가지 않겠다고 약속했다.

  1. 이 단계가 너무 작고 이상해보여도 TDD의 핵심은 이런 작은 단계를 밟을 줄 아는 능력을 가지는 것이라고 한다. 일이 꼬일 경우 차근차근 해결해갈 수 있는 능력을 가질 수 있는 능력. 

  2. 중요한 점은 Dollar 코드를 복사하기 이전에 Ch3, Ch4를 통해 코드를 단순화 해놓은 점이 큰 도움이 되었다는 것.