저번 글 의식적인 연습으로 TDD, 리팩토링 연습하기 에서 학습한 내용과 최범균님의 TDD 실습을 학습하며, 이번 로또 미션에 TDD를 적용해보기로 했다.
이번 글에서는 내가 어떻게 TDD를 적용했는 지 작성하고자 한다.
나는 이번 미션을 진행하며, README에 작성했던 순서대로 테스트 코드를 작성했다.
내가 TDD를 구현하며, 가장 많이 참고했던 자료는 최범균님의 세미나 공유 - TDD 소개 및 시연이다.
굉장히 도움이 되는 내용이 많으니, 한 번 보면 좋을 것 같다.
기본적으로 나는 이번 미션의 요구 사항인 도메인에 대한 테스트에 집중해서 테스트 코드를 작성했다.
순서는 다음과 같다.
1. 실행이 되지 않는 테스트 코드를 작성한다.
2. 그에 맞춰 Test 패키지 안에 프로덕션 코드를 작성한다.
3. 테스트 코드를 Run 시켜 작동이 되는 걸 확인한다.
4. 프로덕션 코드를 리팩토링한다.
5. 테스트를 다시 Run 시켜서 작동이 된다면 Main 패키지로 이동시킨다.
package lotto.domain;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class UserTest {
@Test
@DisplayName("구입한 금액이 1000원 단위가 아닐 경우 예외가 발생한다.")
public void 구입_금액_천원_단위_테스트() throws Exception {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
new User().purchaseLotto("1500");
});
}
}
현재 이 상태에서는 User가 존재하지 않기 때문에 빨간불이 뜨며, 정상적으로 테스트가 되지 않는다.
package lotto.domain;
public class User {
public void purchaseLotto(String purchaseAmount) {
throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위여야 합니다.");
}
}
위에 테스트 코드에 맞춰서 프로덕션 코드를 작성한다.
조건에 맞게 수정한다.
package lotto.domain;
public class User {
public void purchaseLotto(String purchaseAmount) {
int purchasedAmount = Integer.parseInt(purchaseAmount);
if (purchasedAmount % 1000 != 0) {
throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위여야 합니다.");
}
}
}
같은 방식으로 다른 예외 상황 테스트를 작성해보겠다.
package lotto.domain;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class UserTest {
@Test
@DisplayName("구입한 금액이 1000원 단위가 아닐 경우 예외가 발생한다.")
public void 구입_금액_천원_단위_테스트() throws Exception {
[...]
}
@Test
@DisplayName("구입한 금액에 문자열이 입력되면 예외가 발생한다.")
public void 구입_금액_문자열_테스트() throws Exception {
assertThrows(IllegalArgumentException.class, () -> {
new User().purchaseLotto("aaa");
});
}
}
package lotto.domain;
public class User {
public void purchaseLotto(String purchaseAmount) {
if (!purchaseAmount.matches("\\d+")) {
throw new IllegalArgumentException("[ERROR] 구입 금액은 숫자로만 입력되어야 합니다.");
}
[...]
}
}
package lotto.domain;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class UserTest {
@Test
@DisplayName("구입한 금액이 1000원 단위가 아닐 경우 예외가 발생한다.")
public void 구입_금액_천원_단위_테스트() throws Exception {
[...]
}
@Test
@DisplayName("구입한 금액에 문자열이 입력되면 예외가 발생한다.")
public void 구입_금액_문자열_테스트() throws Exception {
[...]
}
@Test
public void 구입_금액_최소_테스트() throws Exception {
assertThrows(IllegalArgumentException.class, () -> {
new User().purchaseLotto("0");
});
}
}
package lotto.domain;
public class User {
public void purchaseLotto(String purchaseAmount) {
[...]
if(purchasedAmount == 0){
throw new IllegalArgumentException("[ERROR] 구입 금액은 최소 1000원입니다.");
}
}
}
package lotto.domain;
public class User {
public void purchaseLotto(String purchaseAmount) {
validatePurchaseAmount(purchaseAmount);
}
private void validatePurchaseAmount(String purchaseAmount){
validateOnlyNumberAmount(purchaseAmount);
int purchasedAmount = Integer.parseInt(purchaseAmount);
validate1000UnitAmount(purchasedAmount);
validateZeroAmount(purchasedAmount);
}
private void validateOnlyNumberAmount(String purchaseAmount) {
if (!purchaseAmount.matches("\\d+")) {
throw new IllegalArgumentException("[ERROR] 구입 금액은 숫자로만 입력되어야 합니다.");
}
}
private void validateZeroAmount(int purchasedAmount) {
if(purchasedAmount == 0){
throw new IllegalArgumentException("[ERROR] 구액 금액은 최소 1000원입니다.");
}
}
private void validate1000UnitAmount(int purchasedAmount) {
if (purchasedAmount % 1000 != 0) {
throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위여야 합니다.");
}
}
}
테스트 코드 확인 후 이상없이 작동된다면 Main 패키지로 이동한다.
TDD 방식으로 구현하면서 좋았던 점은 예외 상황을 미리 컨트롤 할 수 있었다.
문자로 입력되거나 0원이 입력되는 케이스는 기존에 README를 작성하며 생각하지 못했다.
Test code를 작성하며 어떤 점을 테스트 할 수 있을 지 계속해서 생각하며 탐구하니 찾을 수 있었다.
리팩토링을 하는 과정에서 테스트 코드도 수정해야 하는 상황이 발생했다.
예를 들어 값 변경을 시키고 싶지 않아서 final로 선언해야 하는 상황인데, 그렇게 하려면 생성자로 초기화를 해야해서 테스트 코드를 바꿔야 하는 일이 생겼다.
초기에 테스트 코드를 잘못 작성해서 생긴 문제였다.
package lotto.domain;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class LottoWinningNumbersTest {
@Test
@DisplayName("입력받은 당첨 번호가 6개보다 많으면 예외가 발생한다.")
public void 당첨_번호_6개보다_많은_경우() throws Exception {
//given
String winningNumbersString = "1,2,3,4,5,6,7";
//when
List<Integer> winningNumbers = Arrays.stream(winningNumbersString.split(","))
.map(Integer::parseInt)
.collect(Collectors.toList());
//then
assertThrows(IllegalArgumentException.class, () -> new LottoWinningNumbers(winningNumbers));
};
@Test
@DisplayName("입력받은 당첨 번호가 6개보다 적으면 예외가 발생한다.")
public void 당첨_번호_6개보다_적은_경우_테스트() throws Exception {
String winingNumbers = "1,2,3,4,5"`
[...]
};
}
package lotto.domain;
import java.util.List;
public class LottoWinningNumbers {
private final List<Integer> winningNumbers;
public LottoWinningNumbers(List<Integer> winningNumbers) {
validateWinningNumbersSize(winningNumbers);
this.winningNumbers = winningNumbers;
}
private void validateWinningNumbersSize(List<Integer> winningNumbers){
if(winningNumbers.size() != 6){
throw new IllegalArgumentException("[ERROR] 당첨 번호는 6글자로 입력되어야 합니다.");
}
}
}
순서
String 하나 생성 List로 파싱 → 생성자로 던져줌 → 주황불 없애기 → LottoWinningNumbers 클래스 만듬
이전 User에서 final로 선언할 때 생성자로 던져줘야 했었기 때문에 이번엔 미리 생성자로 생성해서 리팩토링 과정 최소화
7글자 입력들어와서 예외처리 확인하면 5글자도 확인하는 테스트를 만들었다.
중복되는 코드를 메서드로 분리했다.
package lotto.domain;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class LottoWinningNumbersTest {
@Test
@DisplayName("입력받은 당첨 번호가 6개보다 많으면 예외가 발생한다.")
public void 당첨_번호_6개보다_많은_경우() throws Exception {
[...]
//when
List<Integer> winningNumbers = convertToIntegerList(winningNumbersString);
[...]
}
@Test
@DisplayName("입력받은 당첨 번호가 6개보다 적으면 예외가 발생한다.")
public void 당첨_번호_6개보다_적은_경우() throws Exception {
//given
[...]
//when
List<Integer> winningNumbers = convertToIntegerList(winningNumbersString);
[...]
}
private List<Integer> convertToIntegerList(String numbersString) {
return Arrays.stream(numbersString.split(","))
.map(Integer::parseInt)
.collect(Collectors.toList());
}
}
package lotto.domain;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class LottoWinningNumbersTest {
[...]
@Test
public void 당첨_번호_범위() throws Exception {
//given
String winningNumberZeroString = "0,1,2,3,4,5";
String winningNumberOverString = "1,2,3,4,5,46";
//when
List<Integer> winningNumberZero = convertToIntegerList(winningNumberZeroString);
List<Integer> winningNumberOver = convertToIntegerList(winningNumberOverString);
//then
assertThrows(IllegalArgumentException.class, () -> new LottoWinningNumbers(winningNumberZero));
assertThrows(IllegalArgumentException.class, () -> new LottoWinningNumbers(winningNumberOver));
}
[...]
}
package lotto.domain;
import java.util.List;
public class LottoWinningNumbers {
private final List<Integer> winningNumbers;
public LottoWinningNumbers(List<Integer> winningNumbers) {
validateWinningNumbersSize(winningNumbers);
validateWinningNumberRange(winningNumbers);
this.winningNumbers = winningNumbers;
}
private void validateWinningNumbersSize(List<Integer> winningNumbers){
[...]
}
public void validateWinningNumberRange(List<Integer> winningNumbers){
for(int i = 0; i < winningNumbers.size(); i++){
if(winningNumbers.get(i) <= 0 || winningNumbers.get(i) > 45){
throw new IllegalArgumentException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
}
}
}
package lotto.domain;
[...]
public class LottoWinningNumbersTest {
[...]
@Test
@DisplayName("입력받은 당첨 범위에 중복이 있을 경우 예외가 발생한다.")
public void 당첨_번호_중복() throws Exception {
//given
String winnerNumberDuplicateString = "1,1,2,3,4,5";
//when
List<Integer> winningNumberDuplicate = convertToIntegerList(winnerNumberDuplicateString);
//then
assertThrows(IllegalArgumentException.class, () -> new LottoWinningNumbers(winningNumberDuplicate));
}
[...]
}
package lotto.domain;
[...]
public class LottoWinningNumbers {
private final List<Integer> winningNumbers;
public LottoWinningNumbers(List<Integer> winningNumbers) {
[...]
validateWinningNumberDuplicate(winningNumbers);
this.winningNumbers = winningNumbers;
}
private void validateWinningNumbersSize(List<Integer> winningNumbers) {
[...]
}
private void validateWinningNumberRange(List<Integer> winningNumbers) {
[...]
}
private void validateWinningNumberDuplicate(List<Integer> winningNumbers) {
Set<Integer> winningNumbersDuplicate = new HashSet<>();
for (Integer number : winningNumbers) {
if (!winningNumbersDuplicate.add(number)) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 중복이 입력될 수 없습니다.");
}
}
}
}
[...]
public class LottoWinningNumbers {
private final List<Integer> winningNumbers;
public LottoWinningNumbers(List<Integer> winningNumbers) {
validateWinningNumbers(winningNumbers);
this.winningNumbers = winningNumbers;
}
private void validateWinningNumbers(List<Integer> winningNumbers){
validateWinningNumbersSize(winningNumbers);
validateWinningNumberRange(winningNumbers);
validateWinningNumberDuplicate(winningNumbers);
}
private void validateWinningNumbersSize(List<Integer> winningNumbers) {
[...]
}
private void validateWinningNumberRange(List<Integer> winningNumbers) {
if (winningNumbers.stream().anyMatch(number -> number <= 0 || number > 45)) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
}
private void validateWinningNumberDuplicate(List<Integer> winningNumbers) {
if (winningNumbers.stream().distinct().count() != winningNumbers.size()) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 중복이 입력될 수 없습니다.");
}
}
}
테스트 코드 확인 후 이상없이 작동된다면 Main 패키지로 이동한다.
여기서 List 자료형으로 가지려고 했기 때 파싱할 때 정수가 아닌 게 들어오는 지 검사하려면 String → List 파싱할 때 해줘야 한다.
물론 NumberFormatException 던져주겠지만 IllegalArgument를 던지기 위해서 작업이 필요하다.
근데 이렇게 구현한 거 자체가 DTO → List로 만드려고 했던 것이다.
그래서 당시에 테스트 코드와 프로덕션 코드를 작성할 때는 잘못된 입력에 대한 처리는 테스트 하지 않고 DTO를 생성하고 만드려주려고 넘어갔었다.
회고록에서 이야기하겠지만, 여기서 고민한 내용때문에 정말 오래 그리고 깊게 생각했고 덕분에 테스트 코드만 이틀은 잡고 고민했었다.
그리고 덕분에 다음 미션에 대한 방향성도 어느 정도 잡히게 되었다.
우선 보너스 번호를 입력받는 테스트 코드를 작성하기 전에 고민했던 것이 있다.
당첨 번호와 보너스 번호를 같은 객체에서 관리할 것인가 였다.
사실 난 당시에는 단순히 이번 미션에서 중요하게 생각했던 점이 도메인의 분리였기 때문에 두 개로 나누었으나,
나중에 수익률을 계산하기 위해 메서드 매개 변수로 3개의 입력을 줘야하는 아이러니한 상황이 발생했다.
아마 돌아간다면 하나로 관리했을 것 같다.
당시 나는 3가지를 고민했었다.
public class LottoWinningNumbers {
private List<Integer> winningNumbers;
private int bonusNumber;
public LottoWinningNumbers(List<Integer> winningNumbers, int bonusNumber) {
// 유효성 검사 및 초기화
}
// 다른 메서드 및 필드 추가
}
public class LottoWinningNumbers {
private List<Integer> winningNumbers;
public LottoWinningNumbers(List<Integer> winningNumbers) {
// 유효성 검사 및 초기화
}
// 다른 메서드 및 필드 추가
}
public class LottoWinningBonusNumbers {
private int bonusNumber;
public LottoWinningBonusNumbers(int bonusNumber) {
// 유효성 검사 및 초기화
}
// 다른 메서드 및 필드 추가
}
public class LottoWinningNumbers {
private List<Integer> winningNumbers;
private LottoWinningBonusNumbers bonusNumbers;
public LottoWinningNumbers(List<Integer> winningNumbers, LottoWinningBonusNumbers bonusNumbers) {
// 유효성 검사 및 초기화
}
// 다른 메서드 및 필드 추가
}
public class LottoWinningBonusNumbers {
private int bonusNumber;
public LottoWinningBonusNumbers(int bonusNumber) {
// 유효성 검사 및 초기화
}
// 다른 메서드 및 필드 추가
}
나는 고민하다가 클래스를 분리하는 것에 중점을 두고자 2번 별도의 객체로 분리하여 관리했다.
다만 위에서도 언급했듯이, 클린 코드에서는 메서드 인자 개수를 통제하는 것을 중요시하게 여겼는데 이렇게 분리하여 관리하니 오히려 반대로 매개 변수의 수가 늘어나는 단점이 생겼다.
이럴 때는 어떤 선택을 해야 옳은 것인지 조금 더 생각을 해봐야 알 것 같다.
package lotto.domain;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
public class LottoWinningBonusNumberTest {
@Test
public void 보너스_번호_범위() throws Exception {
//given
String winningBonusNumberZeroString = "0";
String winningBonusNumberOverString = "46";
//when
int winningBonusNumberZero = convertToInteger(winningBonusNumberZeroString);
int winningBonusNumberOver = convertToInteger(winningBonusNumberOverString);
//then
assertThrows(IllegalArgumentException.class, () -> {
new LottoWinningBonusNumber(winningBonusNumberZero);
});
assertThrows(IllegalArgumentException.class, () -> {
new LottoWinningBonusNumber(winningBonusNumberOver);
});
}
private int convertToInteger(String winningBonusNumberString) {
//todo: service에서 처리할 로직, 정수에 대한 입력이 아닐 경우 예외 처리
try {
int bonusNumber = Integer.parseInt(winningBonusNumberString);
return bonusNumber;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("[ERROR] 보너스 번호는 숫자로 입력되어야 합니다.");
}
}
}
package lotto.domain;
public class LottoWinningBonusNumber {
private final int winningBonusNumber;
public LottoWinningBonusNumber(int winningBonusNumber) {
validateBonusNumberRange(winningBonusNumber);
this.winningBonusNumber= winningBonusNumber;
}
private void validateBonusNumberRange(int winningBonusNumber){
if(winningBonusNumber < 1 || winningBonusNumber > 45){
throw new IllegalArgumentException("[ERROR] 보너스 번호는 1부터 45 사이의 숫자여야 합니다.");
}
}
}
우선 여기서 내가 별도의 객체로 관리한 이유가 나온다.
여기서 보너스 번호가 당첨 번호에 이미 있으면 생성 안 되게 하려면 어쩔 수 없이 보너스 번호 객체에서 당첨 번호를 알고 있어야 한다.
그래서 winningBonusNumber 생성자에List<Integer>winningNumbers를 매개 변수로 넘겨줬다
왜 그렇게 했나 ?
실제 로또 로직을 생각해보니 어차피 2등에 당첨될 때만 보너스 번호가 필요하다.
그래서 비교할 때는 로또가 일단 5개 맞고 1개 틀린 상태에서 보너스를 비교하면 되는데
객체 자체를 그럼 분리하는게 맞다고 생각해서 당첨 번호랑 보너스 두 개를 분리시켰다.
당첨 번호 도메인에 보너스 번호도 필드값으로 가지고 있으면 더 편하겠지만, 로직상 이게 맞다고 판단함
그래서 LottoWinningBonusNumber의 생성자가 변경된 걸 볼 수 있다.
package lotto.domain;
import java.util.List;
public class LottoWinningBonusNumber {
private final int winningBonusNumber;
public LottoWinningBonusNumber(int winningBonusNumber, List<Integer> winningNumbers) {
validateBonusNumberRange(winningBonusNumber);
validateWinningBonusNumberAlreadyExists(winningBonusNumber, winningNumbers);
this.winningBonusNumber= winningBonusNumber;
}
private void validateBonusNumberRange(int winningBonusNumber){
if(winningBonusNumber < 1 || winningBonusNumber > 45){
throw new IllegalArgumentException("[ERROR] 보너스 번호는 1부터 45 사이의 숫자여야 합니다.");
}
}
private void validateWinningBonusNumberAlreadyExists(int bonusNumber, List<Integer> winningNumbers){
if(winningNumbers.contains(bonusNumber)){
throw new IllegalArgumentException("[ERROR] 보너스 번호가 이미 당첨 번호에 존재합니다.");
}
}
}
package lotto.domain;
import java.util.List;
public class LottoWinningBonusNumber {
private final int winningBonusNumber;
public LottoWinningBonusNumber(int winningBonusNumber, List<Integer> winningNumbers) {
validateBonusNumberRange(winningBonusNumber);
validateWinningBonusNumberAlreadyExists(winningBonusNumber, winningNumbers);
this.winningBonusNumber= winningBonusNumber;
}
private void validateBonusNumberRange(int winningBonusNumber){
if(winningBonusNumber < 1 || winningBonusNumber > 45){
throw new IllegalArgumentException("[ERROR] 보너스 번호는 1부터 45 사이의 숫자여야 합니다.");
}
}
private void validateWinningBonusNumberAlreadyExists(int bonusNumber, List<Integer> winningNumbers){
if(winningNumbers.contains(bonusNumber)){
throw new IllegalArgumentException("[ERROR] 보너스 번호가 이미 당첨 번호에 존재합니다.");
}
}
}
보너스 번호는 리팩토링할 것이 딱히 없었다.
테스트 코드 확인 후 이상없이 작동된다면 Main 패키지로 이동한다.
로또 생성에 관해서는 우테코에서 제공해주는 테스트가 있었다.
거기에 맞게 중복을 검사하는 메소드만 추가해주면 끝났다.
따로 TDD할 게 없었다.
로또 발행에서 내가 생각한 예외처리는 6개, 중복된 숫자, 1~45인데 이미 우테코 측에서 제공해주는 테스트에 처음 두 개가 있고, 범위는 라이브러리 Random에서 이미 예외처리 되어 있었다.
그래서 그냥 1~45 발행시키는 로직이랑 발행된 로또 저장하는 도메인만 구현했다.
package lotto.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import lotto.domain.PurchasedLotto;
import lotto.domain.User;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class LottoServiceTest {
@Test
@DisplayName("구입 금액만큼 로또가 생성되는지 검사한다.")
public void 로또_구매_테스트() throws Exception {
//given
User user = new User("10000");
//when
PurchasedLotto purchasedLotto = LottoService.lottoGenerator(user.getAmount());
//then
assertEquals(10, purchasedLotto.getPurchasedLotto().size());
}
}
package lotto.service;
import camp.nextstep.edu.missionutils.Randoms;
import java.util.ArrayList;
import java.util.List;
import lotto.Lotto;
import lotto.domain.PurchasedLotto;
public class LottoService {
public static PurchasedLotto lottoGenerator(int purchaseAmount) {
int pickCount = purchaseAmount / 1000;
List<Lotto> lottos = new ArrayList<>();
for (int i = 0; i < pickCount; i++) {
List<Integer> purchasedOneLotto = Randoms.pickUniqueNumbersInRange(1, 45, 6);
Lotto lotto = new Lotto(purchasedOneLotto);
lottos.add(lotto);
}
return new PurchasedLotto(lottos);
}
}
테스트 코드 확인 후 이상없이 작동된다면 Main 패키지로 이동한다.
추가로 Service는 거의 바꼈다.
이것도 회고록에 따로 서술하겠다.
우선 DTO를 만들었다. (해당 내용은 또 회고록에 따로 서술하겠다. 할 말이 많다.)
그리고 테스트 코드 작성하고
Service에 로직을 작성하는 TDD로 하기로 했다.
왜 이렇게 했냐
model이랑 view랑 만든 다음 레고처럼 Controller를 제일 마지막에 구현해서 조립하고 싶었다.
근데 직접 테스트 하려면 Controller가 필요했다.
그래서 테스트로 내 코드가 맞는 지 확인하고 싶어서 만든 테스트다.
약간 무식한 방법의 테스트였다고 생각한다.
물론 이 코드는 프로덕션 코드를 작성하며 약간의 수정이 있었다.
@Test
@DisplayName("생성된 로또가 출력되는지 검사한다.")
public void 로또_출력() throws Exception {
//given
Lotto lotto1 = new Lotto(List.of(1, 2, 3, 4, 5, 6));
Lotto lotto2 = new Lotto(List.of(10, 11, 12, 13, 14, 15));
Lotto lotto3 = new Lotto(List.of(40, 41, 42, 43, 44, 45));
List<Lotto> lottos = new ArrayList<>();
lottos.add(lotto1);
lottos.add(lotto2);
lottos.add(lotto3);
//when
PurchasedLottoNumbers purchasedLottoNumbers = new PurchasedLottoNumbers(lottos);
PurchasedLottoDTO purchasedLottoDTO = LottoService.purchasedLottoToDTO(purchasedLottoNumbers);
//then
String lottoDTO1 = Arrays.toString(purchasedLottoDTO.getPurchasedLotto().get(0).getNumbers().toArray());
String lottoDTO2 = Arrays.toString(purchasedLottoDTO.getPurchasedLotto().get(1).getNumbers().toArray());
String lottoDTO3 = Arrays.toString(purchasedLottoDTO.getPurchasedLotto().get(2).getNumbers().toArray());
Assertions.assertEquals("[1, 2, 3, 4, 5, 6]", lottoDTO1);
Assertions.assertEquals("[10, 11, 12, 13, 14, 15]", lottoDTO2);
Assertions.assertEquals("[40, 41, 42, 43, 44, 45]", lottoDTO3);
}
TDD에 관해서 찾다보니 아직까지 많은 논쟁이 있는 것 같다.
나도 사용하기 전에는 반대 입장처럼 프로덕션 코드를 작성하지 않고 테스트 코드를 작성한다는 것이 마치 화장실을 가기 전에 손을 씻고 나올 때 안 씻는 거 아닐까? 라는 생각을 했다.
막상 TDD 방식으로 작성하고 나니 생각보다 안정적인 개발을 할 수 있고 장점이 많다는 생각이 들었다.
제일 좋았던 점은 미리 테스트 케이스를 처리하니 그 후로 안정적인 코드를 작성할 수 있다는 점이다.
다만, 단점에서도 언급했듯이 리팩토링을 하는 과정에서 테스트 코드를 계속 수정하는 점이 마음에 걸렸다.
TDD를 제대로 구현하지 못해 발생한 문제일 수도 있다.
아마 당분간은 TDD 방식으로 개발하는 걸 연습해볼 예정이다.
연습하다보면 단점으로 생각했던 점도 장점이 될 수 있을 것이다.
역시 모험가 석환님이시네요! 일부로 메이플스토리 직업도 모험가로 만렙 찍으셨다는데 열심히하는 모습 보기 좋습니다