테스트 주도 개발(TDD) - 1부 요약
Kent Beck은 TDD를 활용하여 점진적으로 다중 통화 시스템을 구축하는 과정을 보여줍니다.
TDD의 기본 주기는 다음과 같습니다:
- 실패하는 테스트 작성
- 테스트를 통과하는 최소한의 구현
- 리팩토링
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장: 돌아온 '모두를 위한 평등'
Dollar
와 Franc
이 동일한 로직을 공유하도록 부모 클래스를 만듭니다.
리팩토링된 코드
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 |