diff --git a/README.md b/README.md
index 5fa2560..244cad1 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,130 @@
# java-lotto-precourse
+
+> 로또 맞게 해주세요...ㅠ
+
+
+ 과제 세부 내용
+
+## 과제 내용
+로또 게임 기능을 구현해야 한다. 로또 게임은 아래와 같은 규칙으로 진행된다.
+```
+- 로또 번호의 숫자 범위는 1~45까지이다.
+- 1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다.
+- 당첨 번호 추첨 시 중복되지 않는 숫자 6개와 보너스 번호 1개를 뽑는다.
+- 당첨은 1등부터 5등까지 있다. 당첨 기준과 금액은 아래와 같다.
+ - 1등: 6개 번호 일치 / 2,000,000,000원
+ - 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원
+ - 3등: 5개 번호 일치 / 1,500,000원
+ - 4등: 4개 번호 일치 / 50,000원
+ - 5등: 3개 번호 일치 / 5,000원
+```
+- 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다.
+- 로또 1장의 가격은 1,000원이다.
+- 당첨 번호와 보너스 번호를 입력받는다.
+- 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다.
+- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.
+ - `Exception`이 아닌 `IllegalArgumentException`, `IllegalStateException` 등과 같은 명확한 유형을 처리한다.
+
+### 입출력
+- 입력
+ - 로또 구입 금액
+ - 당첨 번호 6개
+ - 보너스 번호
+- 출력
+ - 발행한 로또 수량 및 번호
+ - 당첨 내역
+ - 수익률
+ - (예외 문구)
+
+ex)
+
+```
+구입금액을 입력해 주세요.
+8000
+
+8개를 구매했습니다.
+[8, 21, 23, 41, 42, 43]
+[3, 5, 11, 16, 32, 38]
+[7, 11, 16, 35, 36, 44]
+[1, 8, 11, 31, 41, 42]
+[13, 14, 16, 38, 42, 45]
+[7, 11, 30, 40, 42, 43]
+[2, 13, 22, 32, 38, 45]
+[1, 3, 5, 14, 22, 45]
+
+당첨 번호를 입력해 주세요.
+1,2,3,4,5,6
+
+보너스 번호를 입력해 주세요.
+7
+
+당첨 통계
+---
+3개 일치 (5,000원) - 1개
+4개 일치 (50,000원) - 0개
+5개 일치 (1,500,000원) - 0개
+5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
+6개 일치 (2,000,000,000원) - 0개
+총 수익률은 62.5%입니다.
+```
+
+
+
+## 코드 흐름
+- 로또 구매 금액을 입력받는다.
+- 로또를 번호를 생성하고 출력한다.
+- 당첨 번호를 입력받는다.
+- 결과를 계산한 후 출력한다.
+
+```mermaid
+sequenceDiagram
+ participant View
+ participant Controller
+ participant Model
+
+ Controller->>View: 금액 입력 요청
+ View->>Controller: 구입 금액 반환
+ Controller->>Model: 구입 금액 전달
+ Model-->Model: 로또 생성
+ Model->>Controller: 로또 번호 반환
+ Controller->>View: 로또 번호 출력
+ View->>Controller: -
+ Controller->>View: 당첨 번호 입력 요청
+ View->>Controller: 당첨 번호 반환
+ Controller->>Model: 당첨 번호 전달
+ Model-->Model: 당첨 여부 확인
+ Model->>Controller: 결과 반환
+ Controller->>View: 결과 출력
+ View->>Controller: -
+ Controller-->Controller: 프로그램 종료
+
+```
+
+## 구현 기능 목록
+- 입출력
+ - [ ] 구입 금액 입력
+ - [ ] 당첨 번호 입력
+ - [ ] 보너스 번호 입력
+ - [ ] 발행한 로또 수량 및 번호
+ - [ ] 당첨 내역
+ - [ ] 수익률
+ - [ ] 예외
+- 로또
+ - [ ] 로또 생성
+ - [ ] 당첨 확인
+ - [ ] 수익률 계산
+
+## 처리할 예외
+- 나누어 떨어지지 않는 금액 (1000 단위로 떨어지지 않을 때)
+ - `[ERROR] 1,000원 단위로 입력해 주세요.`
+- 너무 큰 구입 금액
+ - `[ERROR] $입력한 금액 보다 작은 금액을 입력해 주세요.`
+- 부적절한 구입 금액 (음수 입력 등 포함, 입력 예외)
+ - `[ERROR] 유효한 구입 금액을 입력해 주세요`
+- 부적절한 로또의 범위 (1-45 밖의 숫자)
+ - `[ERROR] 1부터 45 사이의 값을 입력해 주세요.`
+- 중복되는 번호 / 보너스 번호
+ - `[ERROR] 중복되지 않은 번호를 입력해주세요.`
+- 부적절한 양식 혹은 갯수
+ - `[ERROR] 당첨 번호 6개를 정확히 입력해주세요. ex)1,2,3,4,5,6`
+ - `[ERROR] 보너스 번호 하나를 정확히 입력해주세요. ex)7`
\ No newline at end of file
diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java
index d190922..636a51d 100644
--- a/src/main/java/lotto/Application.java
+++ b/src/main/java/lotto/Application.java
@@ -1,7 +1,19 @@
package lotto;
+import lotto.adapter.in.InputTerminal;
+import lotto.adapter.in.RandomLibrary;
+import lotto.adapter.out.OutputTerminal;
+import lotto.application.service.LottoService;
+
public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
+ LottoService lottoService = new LottoService(
+ new InputTerminal(),
+ new RandomLibrary(),
+ new OutputTerminal()
+ );
+
+ lottoService.run();
}
}
diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java
deleted file mode 100644
index 88fc5cf..0000000
--- a/src/main/java/lotto/Lotto.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package lotto;
-
-import java.util.List;
-
-public class Lotto {
- private final List numbers;
-
- public Lotto(List numbers) {
- validate(numbers);
- this.numbers = numbers;
- }
-
- private void validate(List numbers) {
- if (numbers.size() != 6) {
- throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다.");
- }
- }
-
- // TODO: 추가 기능 구현
-}
diff --git a/src/main/java/lotto/adapter/in/InputTerminal.java b/src/main/java/lotto/adapter/in/InputTerminal.java
new file mode 100644
index 0000000..d4be7a0
--- /dev/null
+++ b/src/main/java/lotto/adapter/in/InputTerminal.java
@@ -0,0 +1,16 @@
+package lotto.adapter.in;
+
+import camp.nextstep.edu.missionutils.Console;
+import lotto.application.port.inport.GetLottoPropertyUseCase;
+
+public class InputTerminal implements GetLottoPropertyUseCase {
+ @Override
+ public String getString() {
+ return Console.readLine();
+ }
+
+ @Override
+ public int getInteger() {
+ return Integer.parseInt(Console.readLine());
+ }
+}
diff --git a/src/main/java/lotto/adapter/in/RandomLibrary.java b/src/main/java/lotto/adapter/in/RandomLibrary.java
new file mode 100644
index 0000000..f67d6bd
--- /dev/null
+++ b/src/main/java/lotto/adapter/in/RandomLibrary.java
@@ -0,0 +1,12 @@
+package lotto.adapter.in;
+
+import camp.nextstep.edu.missionutils.Randoms;
+import java.util.List;
+import lotto.application.port.inport.GetRandomUseCase;
+
+public class RandomLibrary implements GetRandomUseCase {
+ @Override
+ public List getRandomNumbers(int min, int max, int amount) {
+ return Randoms.pickUniqueNumbersInRange(min, max, amount);
+ }
+}
diff --git a/src/main/java/lotto/application/domain/Lotto.java b/src/main/java/lotto/application/domain/Lotto.java
new file mode 100644
index 0000000..8bc8a6b
--- /dev/null
+++ b/src/main/java/lotto/application/domain/Lotto.java
@@ -0,0 +1,42 @@
+package lotto.application.domain;
+
+import java.util.List;
+import lotto.utils.Validator;
+
+public class Lotto {
+ private final List numbers;
+
+ private final static int LOTTO_SIZE = 6;
+ private final static int LOWER_BOUND = 1;
+ private final static int UPPER_BOUND = 45;
+
+ public Lotto(List numbers) {
+ validate(numbers);
+ this.numbers = numbers;
+ }
+
+ private void validate(List numbers) {
+ try {
+ Validator.fitSize(numbers, LOTTO_SIZE);
+ Validator.hasNoDuplication(numbers);
+ Validator.isInRange(numbers, LOWER_BOUND, UPPER_BOUND);
+ } catch (RuntimeException e) {
+ throw new IllegalArgumentException("[ERROR] 부적절한 로또 번호 입니다.", e);
+ }
+ }
+
+ // TODO: 추가 기능 구현
+ public boolean isDuplicate(int numbers) {
+ return this.numbers.contains(numbers);
+ }
+
+ public int countMatch(Lotto other) {
+ return (int) numbers.stream()
+ .filter(other.numbers::contains)
+ .count();
+ }
+
+ public boolean isMatch(int bonus) {
+ return numbers.contains(bonus);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/lotto/application/domain/dto/WinningDataDto.java b/src/main/java/lotto/application/domain/dto/WinningDataDto.java
new file mode 100644
index 0000000..433b01c
--- /dev/null
+++ b/src/main/java/lotto/application/domain/dto/WinningDataDto.java
@@ -0,0 +1,6 @@
+package lotto.application.domain.dto;
+
+import lotto.application.domain.enums.LottoPrize;
+
+public record WinningDataDto(LottoPrize lottoPrize, int count) {
+}
\ No newline at end of file
diff --git a/src/main/java/lotto/application/domain/enums/LottoPrize.java b/src/main/java/lotto/application/domain/enums/LottoPrize.java
new file mode 100644
index 0000000..10a49d0
--- /dev/null
+++ b/src/main/java/lotto/application/domain/enums/LottoPrize.java
@@ -0,0 +1,51 @@
+package lotto.application.domain.enums;
+
+import java.util.List;
+
+public enum LottoPrize {
+ NOTHING("꽝", 0),
+ FIFTH_PRICE("3개 일치", 5_000),
+ FOURTH_PRICE("4개 일치", 50_000),
+ THIRD_PRICE("5개 일치", 1_500_000),
+ SECOND_PRICE("5개 일치, 보너스 볼 일치", 30_000_000),
+ FIRST_PRICE("6개 일치", 2_000_000_000);
+
+ private final String condition;
+ private final int price;
+
+ LottoPrize(String condition, int price) {
+ this.condition = condition;
+ this.price = price;
+ }
+
+ public String getCondition() {
+ return condition;
+ }
+
+ public int getPrice() {
+ return price;
+ }
+
+ public static List getPrizeTypes() {
+ return List.of(FIFTH_PRICE, FOURTH_PRICE, THIRD_PRICE, SECOND_PRICE, FIRST_PRICE);
+ }
+
+ public static LottoPrize getLottoPrize(int matchCount, boolean matchBonus) {
+ if (matchCount == 6) {
+ return FIRST_PRICE;
+ }
+ if (matchCount == 5 && matchBonus) {
+ return SECOND_PRICE;
+ }
+ if (matchCount == 5) {
+ return THIRD_PRICE;
+ }
+ if (matchCount == 4) {
+ return FOURTH_PRICE;
+ }
+ if (matchCount == 3) {
+ return FIFTH_PRICE;
+ }
+ return NOTHING;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/lotto/application/domain/enums/MessageConstants.java b/src/main/java/lotto/application/domain/enums/MessageConstants.java
new file mode 100644
index 0000000..593ddbb
--- /dev/null
+++ b/src/main/java/lotto/application/domain/enums/MessageConstants.java
@@ -0,0 +1,21 @@
+package lotto.application.domain.enums;
+
+public enum MessageConstants {
+ PURCHASE_GUIDE("구입금액을 입력해 주세요."),
+ PURCHASE_RESULT("%d개를 구매했습니다."),
+ WINNING_NUMBER_GUIDE("당첨 번호를 입력해 주세요."),
+ BONUS_NUMBER_GUIDE("보너스 번호를 입력해 주세요."),
+ WINNING_STATUS("당첨 통계\n---"),
+ RESULT_LINE("%s (%,d원) - %d개"),
+ RETURN_RATE("총 수익률은 %.1f%%입니다.")
+
+
+ ;
+ private String message;
+ MessageConstants(String message) {
+ this.message = message;
+ }
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/lotto/application/domain/exceptions/ProgramTerminationException.java b/src/main/java/lotto/application/domain/exceptions/ProgramTerminationException.java
new file mode 100644
index 0000000..83767bd
--- /dev/null
+++ b/src/main/java/lotto/application/domain/exceptions/ProgramTerminationException.java
@@ -0,0 +1,7 @@
+package lotto.application.domain.exceptions;
+
+public class ProgramTerminationException extends IllegalArgumentException {
+ public ProgramTerminationException() {
+ super("[ERROR] 예기치 않은 문제로 프로그램을 종료합니다.");
+ }
+}
diff --git a/src/main/java/lotto/application/port/inport/GetLottoPropertyUseCase.java b/src/main/java/lotto/application/port/inport/GetLottoPropertyUseCase.java
new file mode 100644
index 0000000..064a914
--- /dev/null
+++ b/src/main/java/lotto/application/port/inport/GetLottoPropertyUseCase.java
@@ -0,0 +1,6 @@
+package lotto.application.port.inport;
+
+public interface GetLottoPropertyUseCase {
+ String getString();
+ int getInteger();
+}
diff --git a/src/main/java/lotto/application/port/inport/GetRandomUseCase.java b/src/main/java/lotto/application/port/inport/GetRandomUseCase.java
new file mode 100644
index 0000000..50643ea
--- /dev/null
+++ b/src/main/java/lotto/application/port/inport/GetRandomUseCase.java
@@ -0,0 +1,7 @@
+package lotto.application.port.inport;
+
+import java.util.List;
+
+public interface GetRandomUseCase {
+ List getRandomNumbers(int min, int max, int amount);
+}
diff --git a/src/main/java/lotto/application/port/outport/LottoPort.java b/src/main/java/lotto/application/port/outport/LottoPort.java
new file mode 100644
index 0000000..39af2ad
--- /dev/null
+++ b/src/main/java/lotto/application/port/outport/LottoPort.java
@@ -0,0 +1,12 @@
+package lotto.application.port.outport;
+
+import java.util.List;
+import lotto.application.domain.enums.MessageConstants;
+
+public interface LottoPort {
+ void sendMessage(MessageConstants message);
+ void sendMessage(MessageConstants message, Object... params);
+ void sendMessage(String message);
+ void clear();
+ void printLottoNumbers(List numbers);
+}
diff --git a/src/main/java/lotto/application/service/LottoService.java b/src/main/java/lotto/application/service/LottoService.java
new file mode 100644
index 0000000..c3790ad
--- /dev/null
+++ b/src/main/java/lotto/application/service/LottoService.java
@@ -0,0 +1,132 @@
+package lotto.application.service;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import lotto.application.domain.Lotto;
+import lotto.application.domain.enums.LottoPrize;
+import lotto.application.domain.enums.MessageConstants;
+import lotto.application.domain.exceptions.ProgramTerminationException;
+import lotto.application.port.inport.GetLottoPropertyUseCase;
+import lotto.application.port.inport.GetRandomUseCase;
+import lotto.application.port.outport.LottoPort;
+import lotto.utils.Validator;
+
+public class LottoService {
+ private final GetLottoPropertyUseCase getLottoPropertyUseCase;
+ private final GetRandomUseCase getRandomUseCase;
+ private final LottoPort lottoPort;
+
+ public LottoService(GetLottoPropertyUseCase getLottoPropertyUseCase,
+ GetRandomUseCase getRandomUseCase, LottoPort lottoPort) {
+ this.getLottoPropertyUseCase = getLottoPropertyUseCase;
+ this.getRandomUseCase = getRandomUseCase;
+ this.lottoPort = lottoPort;
+ }
+
+ public void run() {
+ try {
+ final int amount = repeat(this::getAmount);
+ lottoPort.clear();
+ List lottoNumbers = IntStream.range(0, amount).mapToObj(i -> new Lotto(purchase())).toList();
+ lottoPort.clear();
+ final Lotto lotto = repeat(this::getLotto);
+ lottoPort.clear();
+ final int bonus = repeat(() -> getBonusNumber(lotto));
+ lottoPort.clear();
+ printResult(lotto, bonus, lottoNumbers, amount);
+ } catch (ProgramTerminationException e) {
+ lottoPort.sendMessage(e.getMessage());
+ }
+ }
+
+ private int getAmount() {
+ try {
+ lottoPort.sendMessage(MessageConstants.PURCHASE_GUIDE);
+ int res = getLottoPropertyUseCase.getInteger();
+ Validator.isDividedByThousand(res);
+ res /= 1000;
+ lottoPort.sendMessage(MessageConstants.PURCHASE_RESULT, res);
+ return res;
+ } catch (NoSuchElementException e) {
+ throw new ProgramTerminationException();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("[ERROR] 가격이 잘못 입력되었습니다.");
+ }
+ }
+
+ private Lotto getLotto() {
+ try {
+ lottoPort.sendMessage(MessageConstants.WINNING_NUMBER_GUIDE);
+ String res = getLottoPropertyUseCase.getString();
+ return new Lotto(
+ Arrays.stream(res.split(","))
+ .map(String::trim)
+ .map(Integer::parseInt)
+ .toList()
+ );
+ } catch (NoSuchElementException e) {
+ throw new ProgramTerminationException();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("[ERROR] 로또 번호가 잘못 입력되었습니다.");
+ }
+ }
+
+ private int getBonusNumber(Lotto lotto) {
+ try {
+ lottoPort.sendMessage(MessageConstants.BONUS_NUMBER_GUIDE);
+ int res = getLottoPropertyUseCase.getInteger();
+ if (lotto.isDuplicate(res)) {
+ throw new IllegalArgumentException();
+ }
+ return res;
+ } catch (NoSuchElementException e) {
+ throw new ProgramTerminationException();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("[ERROR] 보너스 숫자가 잘못 입력되었습니다.");
+ }
+ }
+
+ private T repeat(Supplier supplier) {
+ while (true) {
+ try {
+ return supplier.get();
+ } catch (ProgramTerminationException e) {
+ throw new ProgramTerminationException();
+ } catch (RuntimeException exception) {
+ lottoPort.sendMessage(exception.getMessage());
+ }
+ }
+ }
+
+ private List purchase() {
+ List numbers = getRandomUseCase.getRandomNumbers(1, 45, 6);
+ lottoPort.printLottoNumbers(numbers);
+ return numbers;
+ }
+
+ private void printResult(Lotto lotto, int bonusNumber, List purchased, int amount) {
+ Map result = purchased.stream()
+ .map(other -> LottoPrize.getLottoPrize(other.countMatch(lotto), other.isMatch(bonusNumber)))
+ .collect(Collectors.groupingBy(
+ Function.identity(),
+ Collectors.counting()
+ ));
+ lottoPort.sendMessage(MessageConstants.WINNING_STATUS);
+ long sum = Stream.of(LottoPrize.FIFTH_PRICE, LottoPrize.FOURTH_PRICE, LottoPrize.THIRD_PRICE,
+ LottoPrize.SECOND_PRICE, LottoPrize.FIRST_PRICE)
+ .peek(prize ->
+ lottoPort.sendMessage(MessageConstants.RESULT_LINE,
+ prize.getCondition(),
+ prize.getPrice(),
+ result.getOrDefault(prize, 0L)))
+ .reduce(0L, (acc, prize) -> acc + (prize.getPrice() * result.getOrDefault(prize, 0L)), Long::sum);
+ lottoPort.sendMessage(MessageConstants.RETURN_RATE, (float) ((double) sum / (1000L * amount) * 100));
+ }
+}
diff --git a/src/main/java/lotto/utils/Validator.java b/src/main/java/lotto/utils/Validator.java
new file mode 100644
index 0000000..17171d8
--- /dev/null
+++ b/src/main/java/lotto/utils/Validator.java
@@ -0,0 +1,38 @@
+package lotto.utils;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class Validator {
+ public static void fitSize(Collection> collection, int size) {
+ if (collection.size() != size) {
+ throw new IllegalStateException("[ERROR] size mismatch");
+ }
+ }
+
+ public static void hasNoDuplication(List itemList) {
+ if (new HashSet<>(itemList).size() != itemList.size()) {
+ throw new IllegalStateException("[ERROR] Duplicated item exist.");
+ };
+ }
+
+ public static void isInRange(int value, int min, int max) {
+ if (value < min || value > max) {
+ throw new IllegalStateException("[ERROR] Invalid range.");
+ }
+ }
+
+ public static void isInRange(List values, int min, int max) {
+ if (!values.stream().allMatch(v -> v >= min && v <= max)) {
+ throw new IllegalStateException("[ERROR] Invalid range.");
+ }
+ }
+
+ public static void isDividedByThousand(int value) {
+ if (value % 1000 != 0) {
+ throw new IllegalStateException("[ERROR] Invalid divided thousand.");
+ }
+ }
+}
diff --git a/src/test/java/lotto/ApplicationTest.java b/src/test/java/lotto/ApplicationTest.java
index a15c7d1..85a4071 100644
--- a/src/test/java/lotto/ApplicationTest.java
+++ b/src/test/java/lotto/ApplicationTest.java
@@ -1,14 +1,18 @@
package lotto;
-import camp.nextstep.edu.missionutils.test.NsTest;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-
import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomUniqueNumbersInRangeTest;
import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest;
import static org.assertj.core.api.Assertions.assertThat;
+import camp.nextstep.edu.missionutils.test.NsTest;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
class ApplicationTest extends NsTest {
private static final String ERROR_MESSAGE = "[ERROR]";
@@ -54,8 +58,106 @@ class ApplicationTest extends NsTest {
});
}
+ @Test
+ void 로또번호_중복_Error1() {
+ assertSimpleTest(() -> {
+ run("5000", "1,2,3,3,5,6", "7");
+ assertThat(output()).contains(ERROR_MESSAGE);
+ });
+ }
+
+ @Test
+ void 로또번호_중복_Error2() {
+ assertSimpleTest(() -> {
+ run("5000", "1,2,3,4,5,6", "4");
+ assertThat(output()).contains(ERROR_MESSAGE);
+ });
+ }
+
+ @Test
+ void 로또번호_범위_Error() {
+ assertSimpleTest(() -> {
+ run("5000", "0,2,3,4,5,6", "7");
+ assertThat(output()).contains(ERROR_MESSAGE);
+
+ run("5000", "1,2,3,4,5,46", "7");
+ assertThat(output()).contains(ERROR_MESSAGE);
+ });
+ }
+
+ @Test
+ void 로또번호_갯수_Error() {
+ assertSimpleTest(() -> {
+ runException("5000", "1,2,3,4,5", "7");
+ assertThat(output()).contains(ERROR_MESSAGE);
+
+ runException("5000", "1,2,3,4,5,6,7", "7");
+ assertThat(output()).contains(ERROR_MESSAGE);
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideNumbersForTest")
+ @DisplayName("로또 당첨 결과 테스트")
+ void 로또_당첨_등수_확인(String purchaseAmount, String winningNumbers, String bonusNumber, String expectedOutput) {
+ assertRandomUniqueNumbersInRangeTest(
+ () -> {
+ run(purchaseAmount, winningNumbers, bonusNumber);
+ assertThat(output()).contains(expectedOutput);
+ },
+ List.of(1, 2, 3, 4, 5, 6)
+ );
+ }
+
+ static Stream provideNumbersForTest() {
+ return Stream.of(
+ Arguments.of("1000", "1,2,3,43,44,45", "7", "3개 일치 (5,000원) - 1개"),
+ Arguments.of("1000", "1,2,3,4,44,45", "7", "4개 일치 (50,000원) - 1개"),
+ Arguments.of("1000", "1,2,3,4,5,45", "7", "5개 일치 (1,500,000원) - 1개"),
+ Arguments.of("1000", "1,2,3,4,5,45", "6", "5개 일치, 보너스 볼 일치 (30,000,000원) - 1개"),
+ Arguments.of("1000", "1,2,3,4,5,6", "7", "6개 일치 (2,000,000,000원) - 1개")
+
+ );
+ }
+
+ @Test
+ void 수익률_반올림_테스트() {
+ assertRandomUniqueNumbersInRangeTest(
+ () -> {
+ run("3000", "1,2,3,4,5,6", "7");
+ try {
+ assertThat(output()).contains(ERROR_MESSAGE);
+ } catch (AssertionError e) {
+ assertThat(output()).contains("총 수익률은 166.7%입니다.");
+ }
+ },
+ List.of(1, 2, 3, 43, 44, 45),
+ List.of(40, 41, 42, 43, 44, 45),
+ List.of(40, 41, 42, 43, 44, 45)
+ );
+ }
+
+ @Test
+ @DisplayName("죽지도 않고 돌아온 정수 오버플로우")
+ void OverflowTest() {
+ assertRandomUniqueNumbersInRangeTest(
+ () -> {
+ run("2000", "1,2,3,4,5,6", "7");
+ try {
+ assertThat(output()).contains(ERROR_MESSAGE);
+ } catch (AssertionError e) {
+ assertThat(output()).contains("6개 일치 (2,000,000,000원) - 2개");
+ assertThat(output()).contains("총 수익률은 200000000.0%입니다.");
+ }
+ },
+ List.of(1, 2, 3, 4, 5, 6),
+ List.of(1, 2, 3, 4, 5, 6),
+ List.of(1, 2, 3, 4, 5, 6)
+ );
+ }
+
@Override
public void runMain() {
Application.main(new String[]{});
}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java
index 309f4e5..b266590 100644
--- a/src/test/java/lotto/LottoTest.java
+++ b/src/test/java/lotto/LottoTest.java
@@ -1,5 +1,6 @@
package lotto;
+import lotto.application.domain.Lotto;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;