테스트 주도 개발(TDD) - 1부 요약

Kent Beck은 TDD를 활용하여 점진적으로 다중 통화 시스템을 구축하는 과정을 보여줍니다.
TDD의 기본 주기는 다음과 같습니다:

  1. 실패하는 테스트 작성
  2. 테스트를 통과하는 최소한의 구현
  3. 리팩토링

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

금액 연산을 지원하면서 여러 통화를 다루는 것이 목표입니다.

코드 구현

public class Dollar {
    int amount;

    Dollar(int amount) {
        this.amount = amount;
    }

    Dollar times(int multiplier) {
        return new Dollar(amount * multiplier);
    }
}

테스트 코드

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

public class MoneyTest {
    @Test
    void testMultiplication() {
        Dollar five = new Dollar(5);
        assertEquals(new Dollar(10), five.times(2));
        assertEquals(new Dollar(15), five.times(3));
    }
}

2장: 타락한 객체

최소한의 구현으로 테스트를 통과하도록 합니다.

  • 테스트 코드는 변경되지 않음 (테스트는 여전히 실패)
  • 구현도 그대로 유지됨.

3장: 모두를 위한 평등

객체를 비교할 수 있도록 equals() 메서드를 구현합니다.

업데이트된 코드

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

테스트 코드

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

책에서 말하는 삼각측량이 제대로 이해가 되지 않았다. 서로 반대되는 경우를 테스트 하라는 건가?


4장: 프라이버시

amount 필드를 private으로 변경하여 캡슐화를 강화합니다.

업데이트된 코드

public class Dollar {
    private int amount;

    // 생성자 및 times 메서드는 변경 없음
}

5장: 솔직히 말하자면

새로운 통화인 Franc을 추가합니다.

새로운 Franc 클래스

public class Franc {
    private int amount;

    Franc(int amount) {
        this.amount = amount;
    }

    Franc times(int multiplier) {
        return new Franc(amount * multiplier);
    }

    @Override
    public boolean equals(Object object) {
        Franc franc = (Franc) object;
        return amount == franc.amount;
    }
}

테스트 코드

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

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

DollarFranc이 동일한 로직을 공유하도록 부모 클래스를 만듭니다.

리팩토링된 코드

public class Money {
    protected int amount;

    @Override
    public boolean equals(Object object) {
        Money money = (Money) object;
        return amount == money.amount;
    }
}
public class Dollar extends Money {
    Dollar(int amount) {
        this.amount = amount;
    }

    Dollar times(int multiplier) {
        return new Dollar(amount * multiplier);
    }
}
public class Franc extends Money {
    Franc(int amount) {
        this.amount = amount;
    }

    Franc times(int multiplier) {
        return new Franc(amount * multiplier);
    }
}

7장: 사과와 오렌지

통화 정보를 추가하여 Money 클래스를 확장합니다.

업데이트된 코드

public class Money {
    protected int amount;
    protected String currency;

    Money(int amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public String currency() {
        return currency;
    }

    @Override
    public boolean equals(Object object) {
        Money money = (Money) object;
        return amount == money.amount && currency.equals(money.currency);
    }
}

테스트 코드

@Test
void testCurrency() {
    assertEquals("USD", new Dollar(1, "USD").currency());
    assertEquals("CHF", new Franc(1, "CHF").currency());
}

8장: 객체 만들기

객체 생성을 단순화하기 위해 팩토리 메서드를 추가합니다.

업데이트된 코드

public class Money {
    protected int amount;
    protected String currency;

    Money(int amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public static Money dollar(int amount) {
        return new Money(amount, "USD");
    }

    public static Money franc(int amount) {
        return new Money(amount, "CHF");
    }
}

테스트 코드

@Test
void testFactoryMethod() {
    Money fiveDollars = Money.dollar(5);
    Money fiveFrancs = Money.franc(5);
    assertEquals("USD", fiveDollars.currency());
    assertEquals("CHF", fiveFrancs.currency());
}

9장: 우리가 사는 시간

곱셈 연산을 Money 클래스로 이동하여 중복을 제거합니다.

업데이트된 코드

public Money times(int multiplier) {
    return new Money(amount * multiplier, currency);
}

테스트 코드

@Test
void testMultiplication() {
    Money five = Money.dollar(5);
    assertEquals(Money.dollar(10), five.times(2));
    assertEquals(Money.dollar(15), five.times(3));
}

10장: 흥미로운 시간

은행(Bank) 클래스를 추가하여 환율 변환을 다룹니다.

새로운 코드

public class Bank {
    Money reduce(Expression source, String toCurrency) {
        return Money.dollar(10); // 임시 구현
    }
}

테스트 코드

@Test
void testSimpleAddition() {
    Money five = Money.dollar(5);
    Expression sum = five.plus(five);
    Bank bank = new Bank();
    Money reduced = bank.reduce(sum, "USD");
    assertEquals(Money.dollar(10), reduced);
}

11장: 모든 악의 근원

Expression 인터페이스를 도입하여 연산을 일반화합니다.

새로운 코드

public interface Expression {
    Money reduce(Bank bank, String toCurrency);
}
public class Sum implements Expression {
    Money augend;
    Money addend;

    Sum(Money augend, Money addend) {
        this.augend = augend;
        this.addend = addend;
    }

    public Money reduce(Bank bank, String toCurrency) {
        int amount = augend.amount + addend.amount;
        return new Money(amount, toCurrency);
    }
}

테스트 코드

@Test
void testReduceSum() {
    Expression sum = new Sum(Money.dollar(3), Money.dollar(4));
    Bank bank = new Bank();
    Money result = bank.reduce(sum, "USD");
    assertEquals(Money.dollar(7), result);
}

'항해99 플러스 > 사전스터디' 카테고리의 다른 글

[TDD] 2주차  (0) 2025.03.18
[TDD방법론 사전스터디] 2주차  (2) 2024.09.03
[Java 스터디] 강의 3주차  (1) 2024.09.03
[TDD방법론 사전스터디] 1주차  (4) 2024.08.26
[Java 스터디] 강의 2주차  (0) 2024.08.21

+ Recent posts