diff --git a/.java-version b/.java-version new file mode 100644 index 00000000..98d9bcb7 --- /dev/null +++ b/.java-version @@ -0,0 +1 @@ +17 diff --git a/README.md b/README.md index 8d7e8aee..a51062f6 100644 --- a/README.md +++ b/README.md @@ -1 +1,37 @@ -# java-baseball-precourse \ No newline at end of file +# 숫자 야구 게임 + +## 프로젝트 개요 +1부터 9까지 서로 다른 숫자로 이루어진 3자리 수를 맞추는 숫자 야구 게임이다. +컴퓨터가 임의의 숫자 3개를 생성하고, 사용자는 숫자를 입력해 힌트를 받으며 정답을 맞힌다. +모든 숫자를 맞히면 게임이 종료되며, 이후 재시작 또는 종료를 선택할 수 있다. + +간단한 흐름 +- 숫자 입력 → 힌트 출력(스트라이크/볼/낫싱) → 정답 시 종료 메시지 출력 → 재시작/종료 선택 + +## 구현(레이어드) 체크리스트 +### Domain +- [x] ComputerNumbers: 1~9 서로 다른 숫자 3개 생성 +- [x] ComputerNumbersTest: 범위/중복 생성 검증 +- [x] PlayerNumbers: 입력 파싱 및 검증(길이/숫자/범위/중복) +- [x] PlayerNumbersTest: 파싱 및 예외 케이스 검증 +- [x] Judgement: 스트라이크/볼 계산 +- [x] JudgementTest: 스트라이크/볼/낫싱 케이스 검증 +- [x] HintResult: 판정 결과 값 객체(스트라이크/볼/낫싱) +- [x] HintResultTest: 결과 값 객체 규칙 검증 + +### Application +- [x] GameService: 정답 생성과 판정 흐름 연결 +- [x] GameService: 3스트라이크 종료 판단 +- [x] GameService: 재시작/종료 분기 처리 + +### UI +- [x] InputView: 숫자 입력, 재시작 선택 입력 +- [x] OutputView: 힌트/종료 메시지 출력 +- [x] ErrorHandler: `[ERROR]` 메시지 출력 후 진행 유지 + +## 제약사항 +- indent depth 2 이하 +- `else`/`switch` 미사용 +- 메서드 길이 15라인 이하 +- Stream API 미사용(람다 가능) +- 도메인 로직과 UI 로직 분리 diff --git a/src/main/java/baseball/BaseballApplication.java b/src/main/java/baseball/BaseballApplication.java new file mode 100644 index 00000000..180bbd72 --- /dev/null +++ b/src/main/java/baseball/BaseballApplication.java @@ -0,0 +1,82 @@ +package baseball; + +import baseball.application.GameService; +import baseball.domain.exceptions.BaseballException; +import baseball.domain.models.ComputerNumbers; +import baseball.domain.models.HintResult; +import baseball.domain.models.PlayerNumbers; +import baseball.ui.ErrorHandler; +import baseball.ui.InputView; +import baseball.ui.OutputView; + +public class BaseballApplication { + private final GameService gameService; + private final InputView inputView; + private final OutputView outputView; + private final ErrorHandler errorHandler; + + public BaseballApplication() { + this(new GameService(new ComputerNumbers()), new InputView(), new OutputView(), new ErrorHandler()); + } + + public BaseballApplication(GameService gameService, InputView inputView, OutputView outputView, + ErrorHandler errorHandler) { + this.gameService = gameService; + this.inputView = inputView; + this.outputView = outputView; + this.errorHandler = errorHandler; + } + + public static void main(String[] args) { + new BaseballApplication().run(); + } + + public void run() { + gameService.startNewGame(); + playLoop(); + } + + private void playLoop() { + while (true) { + HintResult result = playRound(); + if (!gameService.isGameOver(result)) { + continue; + } + outputView.printGameOver(); + if (restartGame()) { + gameService.startNewGame(); + continue; + } + return; + } + } + + private HintResult playRound() { + PlayerNumbers playerNumbers = readPlayerNumbers(); + HintResult result = gameService.judge(playerNumbers); + outputView.printHint(result); + return result; + } + + private PlayerNumbers readPlayerNumbers() { + while (true) { + outputView.printInputPrompt(); + try { + return PlayerNumbers.from(inputView.readNumbers()); + } catch (BaseballException exception) { + errorHandler.printError(exception); + } + } + } + + private boolean restartGame() { + while (true) { + outputView.printRestartPrompt(); + try { + return gameService.shouldRestart(inputView.readRestartOption()); + } catch (BaseballException exception) { + errorHandler.printError(exception); + } + } + } +} diff --git a/src/main/java/baseball/application/GameService.java b/src/main/java/baseball/application/GameService.java new file mode 100644 index 00000000..62c2f98c --- /dev/null +++ b/src/main/java/baseball/application/GameService.java @@ -0,0 +1,66 @@ +package baseball.application; + +import java.util.List; + +import baseball.domain.exceptions.GameNotStartedException; +import baseball.domain.exceptions.InvalidRestartOptionException; +import baseball.domain.models.ComputerNumbers; +import baseball.domain.models.HintResult; +import baseball.domain.models.Judgement; +import baseball.domain.models.PlayerNumbers; + +public class GameService { + private final ComputerNumbers computerNumbers; + private List answerNumbers; + + public GameService(ComputerNumbers computerNumbers) { + this.computerNumbers = computerNumbers; + } + + public void startNewGame() { + answerNumbers = computerNumbers.generate(); + } + + public HintResult judge(PlayerNumbers playerNumbers) { + validateStarted(); + Judgement judgement = new Judgement(answerNumbers, playerNumbers.numbers()); + return new HintResult(judgement.countStrike(), judgement.countBall()); + } + + public boolean isGameOver(HintResult hintResult) { + return hintResult.strikeCount() == 3; + } + + public boolean shouldRestart(String input) { + validateRestartOption(input); + return isRestartOption(input); + } + + private void validateStarted() { + if (answerNumbers != null) { + return; + } + throw new GameNotStartedException("게임을 먼저 시작해주세요."); + } + + private void validateRestartOption(String input) { + if (input == null) { + throw new InvalidRestartOptionException(); + } + if (isRestartOption(input)) { + return; + } + if (isExitOption(input)) { + return; + } + throw new InvalidRestartOptionException(); + } + + private boolean isRestartOption(String input) { + return "1".equals(input); + } + + private boolean isExitOption(String input) { + return "2".equals(input); + } +} diff --git a/src/main/java/baseball/domain/exceptions/BaseballException.java b/src/main/java/baseball/domain/exceptions/BaseballException.java new file mode 100644 index 00000000..63311ec5 --- /dev/null +++ b/src/main/java/baseball/domain/exceptions/BaseballException.java @@ -0,0 +1,9 @@ +package baseball.domain.exceptions; + +public class BaseballException extends IllegalArgumentException { + private static final String ERROR_PREFIX = "[ERROR] "; + + public BaseballException(String message) { + super(ERROR_PREFIX + message); + } +} diff --git a/src/main/java/baseball/domain/exceptions/GameNotStartedException.java b/src/main/java/baseball/domain/exceptions/GameNotStartedException.java new file mode 100644 index 00000000..485b0b9a --- /dev/null +++ b/src/main/java/baseball/domain/exceptions/GameNotStartedException.java @@ -0,0 +1,7 @@ +package baseball.domain.exceptions; + +public class GameNotStartedException extends BaseballException { + public GameNotStartedException(String message) { + super(message); + } +} diff --git a/src/main/java/baseball/domain/exceptions/InvalidHintResultException.java b/src/main/java/baseball/domain/exceptions/InvalidHintResultException.java new file mode 100644 index 00000000..2183c627 --- /dev/null +++ b/src/main/java/baseball/domain/exceptions/InvalidHintResultException.java @@ -0,0 +1,7 @@ +package baseball.domain.exceptions; + +public class InvalidHintResultException extends BaseballException { + public InvalidHintResultException(String message) { + super(message); + } +} diff --git a/src/main/java/baseball/domain/exceptions/InvalidPlayerNumbersException.java b/src/main/java/baseball/domain/exceptions/InvalidPlayerNumbersException.java new file mode 100644 index 00000000..b97ca2c3 --- /dev/null +++ b/src/main/java/baseball/domain/exceptions/InvalidPlayerNumbersException.java @@ -0,0 +1,7 @@ +package baseball.domain.exceptions; + +public class InvalidPlayerNumbersException extends BaseballException { + public InvalidPlayerNumbersException(String message) { + super(message); + } +} diff --git a/src/main/java/baseball/domain/exceptions/InvalidRestartOptionException.java b/src/main/java/baseball/domain/exceptions/InvalidRestartOptionException.java new file mode 100644 index 00000000..c91b0522 --- /dev/null +++ b/src/main/java/baseball/domain/exceptions/InvalidRestartOptionException.java @@ -0,0 +1,9 @@ +package baseball.domain.exceptions; + +public class InvalidRestartOptionException extends BaseballException { + private static final String MESSAGE = "재시작은 1, 종료는 2를 입력해주세요."; + + public InvalidRestartOptionException() { + super(MESSAGE); + } +} diff --git a/src/main/java/baseball/domain/models/ComputerNumbers.java b/src/main/java/baseball/domain/models/ComputerNumbers.java new file mode 100644 index 00000000..d9df929f --- /dev/null +++ b/src/main/java/baseball/domain/models/ComputerNumbers.java @@ -0,0 +1,29 @@ +package baseball.domain.models; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class ComputerNumbers { + private final Random random; + + public ComputerNumbers() { + this(new Random()); + } + + public ComputerNumbers(Random random) { + this.random = random; + } + + public List generate() { + List numbers = new ArrayList<>(); + while (numbers.size() < 3) { + int candidate = random.nextInt(9) + 1; + if (numbers.contains(candidate)) { + continue; + } + numbers.add(candidate); + } + return numbers; + } +} diff --git a/src/main/java/baseball/domain/models/HintResult.java b/src/main/java/baseball/domain/models/HintResult.java new file mode 100644 index 00000000..6351abbf --- /dev/null +++ b/src/main/java/baseball/domain/models/HintResult.java @@ -0,0 +1,61 @@ +package baseball.domain.models; + +import baseball.domain.exceptions.InvalidHintResultException; + +public class HintResult { + private static final int MAX_COUNT = 3; + private static final String NOTHING_MESSAGE = "낫싱"; + private static final String STRIKE_SUFFIX = "스트라이크"; + private static final String BALL_SUFFIX = "볼"; + + private final int strikeCount; + private final int ballCount; + + public HintResult(int strikeCount, int ballCount) { + validateCount(strikeCount); + validateCount(ballCount); + validateTotal(strikeCount, ballCount); + this.strikeCount = strikeCount; + this.ballCount = ballCount; + } + + public int strikeCount() { + return strikeCount; + } + + public int ballCount() { + return ballCount; + } + + public String message() { + if (strikeCount == 0 && ballCount == 0) { + return NOTHING_MESSAGE; + } + StringBuilder builder = new StringBuilder(); + appendCount(builder, strikeCount, STRIKE_SUFFIX); + appendCount(builder, ballCount, BALL_SUFFIX); + return builder.toString(); + } + + private void appendCount(StringBuilder builder, int count, String suffix) { + if (count == 0) { + return; + } + if (builder.length() > 0) { + builder.append(" "); + } + builder.append(count).append(suffix); + } + + private void validateCount(int count) { + if (count < 0 || count > MAX_COUNT) { + throw new InvalidHintResultException("카운트는 0에서 3 사이여야 해요."); + } + } + + private void validateTotal(int strikeCount, int ballCount) { + if (strikeCount + ballCount > MAX_COUNT) { + throw new InvalidHintResultException("스트라이크와 볼의 합은 3을 넘을 수 없어요."); + } + } +} diff --git a/src/main/java/baseball/domain/models/Judgement.java b/src/main/java/baseball/domain/models/Judgement.java new file mode 100644 index 00000000..53d300f0 --- /dev/null +++ b/src/main/java/baseball/domain/models/Judgement.java @@ -0,0 +1,41 @@ +package baseball.domain.models; + +import java.util.ArrayList; +import java.util.List; + +public class Judgement { + private final List computerNumbers; + private final List playerNumbers; + + public Judgement(List computerNumbers, List playerNumbers) { + this.computerNumbers = new ArrayList<>(computerNumbers); + this.playerNumbers = new ArrayList<>(playerNumbers); + } + + public int countStrike() { + int strikes = 0; + for (int index = 0; index < playerNumbers.size(); index++) { + if (isStrike(index)) { + strikes++; + } + } + return strikes; + } + + public int countBall() { + int balls = 0; + for (int index = 0; index < playerNumbers.size(); index++) { + if (isStrike(index)) { + continue; + } + if (computerNumbers.contains(playerNumbers.get(index))) { + balls++; + } + } + return balls; + } + + private boolean isStrike(int index) { + return playerNumbers.get(index).equals(computerNumbers.get(index)); + } +} diff --git a/src/main/java/baseball/domain/models/PlayerNumbers.java b/src/main/java/baseball/domain/models/PlayerNumbers.java new file mode 100644 index 00000000..3b313774 --- /dev/null +++ b/src/main/java/baseball/domain/models/PlayerNumbers.java @@ -0,0 +1,74 @@ +package baseball.domain.models; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import baseball.domain.exceptions.InvalidPlayerNumbersException; + +public class PlayerNumbers { + private static final int REQUIRED_LENGTH = 3; + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 9; + private final List numbers; + + private PlayerNumbers(List numbers) { + this.numbers = numbers; + } + + public static PlayerNumbers from(String input) { + return new PlayerNumbers(parseNumbers(input)); + } + + public List numbers() { + return new ArrayList<>(numbers); + } + + private static List parseNumbers(String input) { + validateNotNull(input); + validateLength(input); + List numbers = new ArrayList<>(); + Set uniqueNumbers = new HashSet<>(); + for (int index = 0; index < input.length(); index++) { + int number = parseDigit(input, index); + validateNumber(number); + validateDuplicate(uniqueNumbers, number); + numbers.add(number); + } + return numbers; + } + + private static void validateNotNull(String input) { + if (input == null) { + throw new InvalidPlayerNumbersException("입력값을 제공해야 해요."); + } + } + + private static void validateLength(String input) { + if (input.length() != REQUIRED_LENGTH) { + throw new InvalidPlayerNumbersException("입력은 3자리여야 해요."); + } + } + + private static int parseDigit(String input, int index) { + char value = input.charAt(index); + if (Character.isDigit(value)) { + return Character.getNumericValue(value); + } + throw new InvalidPlayerNumbersException("입력은 숫자만 포함해야 해요."); + } + + private static void validateNumber(int number) { + if (number < MIN_NUMBER || number > MAX_NUMBER) { + throw new InvalidPlayerNumbersException("각 자리는 1부터 9 사이여야 해요."); + } + } + + private static void validateDuplicate(Set uniqueNumbers, int number) { + if (uniqueNumbers.add(number)) { + return; + } + throw new InvalidPlayerNumbersException("숫자는 중복되면 안 돼요."); + } +} diff --git a/src/main/java/baseball/ui/ErrorHandler.java b/src/main/java/baseball/ui/ErrorHandler.java new file mode 100644 index 00000000..4c22953e --- /dev/null +++ b/src/main/java/baseball/ui/ErrorHandler.java @@ -0,0 +1,9 @@ +package baseball.ui; + +import baseball.domain.exceptions.BaseballException; + +public class ErrorHandler { + public void printError(BaseballException exception) { + System.out.println(exception.getMessage()); + } +} diff --git a/src/main/java/baseball/ui/InputView.java b/src/main/java/baseball/ui/InputView.java new file mode 100644 index 00000000..b5519d17 --- /dev/null +++ b/src/main/java/baseball/ui/InputView.java @@ -0,0 +1,26 @@ +package baseball.ui; + +import java.util.Scanner; + +public class InputView { + private final Scanner scanner; + + public InputView() { + this.scanner = new Scanner(System.in); + } + + public String readNumbers() { + return readLine(); + } + + public String readRestartOption() { + return readLine(); + } + + private String readLine() { + if (!scanner.hasNextLine()) { + return ""; + } + return scanner.nextLine().trim(); + } +} diff --git a/src/main/java/baseball/ui/OutputView.java b/src/main/java/baseball/ui/OutputView.java new file mode 100644 index 00000000..472c6866 --- /dev/null +++ b/src/main/java/baseball/ui/OutputView.java @@ -0,0 +1,25 @@ +package baseball.ui; + +import baseball.domain.models.HintResult; + +public class OutputView { + private static final String INPUT_PROMPT = "숫자를 입력해주세요: "; + private static final String GAME_OVER_MESSAGE = "3개의 숫자를 모두 맞히셨습니다! 게임 끝"; + private static final String RESTART_PROMPT = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + + public void printInputPrompt() { + System.out.print(INPUT_PROMPT); + } + + public void printHint(HintResult hintResult) { + System.out.println(hintResult.message()); + } + + public void printGameOver() { + System.out.println(GAME_OVER_MESSAGE); + } + + public void printRestartPrompt() { + System.out.println(RESTART_PROMPT); + } +} diff --git a/src/test/java/baseball/application/GameServiceTest.java b/src/test/java/baseball/application/GameServiceTest.java new file mode 100644 index 00000000..a3de3f52 --- /dev/null +++ b/src/test/java/baseball/application/GameServiceTest.java @@ -0,0 +1,87 @@ +package baseball.application; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import baseball.domain.exceptions.GameNotStartedException; +import baseball.domain.exceptions.InvalidRestartOptionException; +import baseball.domain.models.ComputerNumbers; +import baseball.domain.models.HintResult; +import baseball.domain.models.PlayerNumbers; + +import org.junit.jupiter.api.Test; + +class GameServiceTest { + @Test + void judge_returns_hint_based_on_generated_numbers() { + List numbers = Arrays.asList(4, 2, 5); + GameService service = new GameService(new StubComputerNumbers(numbers)); + + service.startNewGame(); + HintResult result = service.judge(PlayerNumbers.from("452")); + + assertThat(result.strikeCount()).isEqualTo(1); + assertThat(result.ballCount()).isEqualTo(2); + } + + @Test + void judge_throws_when_game_not_started() { + GameService service = new GameService(new StubComputerNumbers(Arrays.asList(1, 2, 3))); + + assertThatThrownBy(() -> service.judge(PlayerNumbers.from("123"))) + .isInstanceOf(GameNotStartedException.class); + } + + @Test + void isGameOver_returns_true_when_three_strikes() { + GameService service = new GameService(new StubComputerNumbers(Arrays.asList(1, 2, 3))); + + assertThat(service.isGameOver(new HintResult(3, 0))).isTrue(); + } + + @Test + void isGameOver_returns_false_when_not_three_strikes() { + GameService service = new GameService(new StubComputerNumbers(Arrays.asList(1, 2, 3))); + + assertThat(service.isGameOver(new HintResult(2, 1))).isFalse(); + } + + @Test + void shouldRestart_returns_true_when_input_is_one() { + GameService service = new GameService(new StubComputerNumbers(Arrays.asList(1, 2, 3))); + + assertThat(service.shouldRestart("1")).isTrue(); + } + + @Test + void shouldRestart_returns_false_when_input_is_two() { + GameService service = new GameService(new StubComputerNumbers(Arrays.asList(1, 2, 3))); + + assertThat(service.shouldRestart("2")).isFalse(); + } + + @Test + void shouldRestart_throws_when_input_is_invalid() { + GameService service = new GameService(new StubComputerNumbers(Arrays.asList(1, 2, 3))); + + assertThatThrownBy(() -> service.shouldRestart("0")) + .isInstanceOf(InvalidRestartOptionException.class); + } + + private static class StubComputerNumbers extends ComputerNumbers { + private final List numbers; + + private StubComputerNumbers(List numbers) { + this.numbers = new ArrayList<>(numbers); + } + + @Override + public List generate() { + return new ArrayList<>(numbers); + } + } +} diff --git a/src/test/java/baseball/domain/models/ComputerNumbersTest.java b/src/test/java/baseball/domain/models/ComputerNumbersTest.java new file mode 100644 index 00000000..5f6dfa2f --- /dev/null +++ b/src/test/java/baseball/domain/models/ComputerNumbersTest.java @@ -0,0 +1,26 @@ +package baseball.domain.models; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +class ComputerNumbersTest { + @Test + void generate_returns_three_unique_numbers_between_one_and_nine() { + ComputerNumbers computerNumbers = new ComputerNumbers(new Random(1)); + + List numbers = computerNumbers.generate(); + + assertThat(numbers).hasSize(3); + Set uniqueNumbers = new HashSet<>(numbers); + assertThat(uniqueNumbers).hasSize(3); + for (int number : numbers) { + assertThat(number).isBetween(1, 9); + } + } +} diff --git a/src/test/java/baseball/domain/models/HintResultTest.java b/src/test/java/baseball/domain/models/HintResultTest.java new file mode 100644 index 00000000..440aa9e6 --- /dev/null +++ b/src/test/java/baseball/domain/models/HintResultTest.java @@ -0,0 +1,56 @@ +package baseball.domain.models; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import baseball.domain.exceptions.InvalidHintResultException; + +import org.junit.jupiter.api.Test; + +class HintResultTest { + @Test + void message_returns_nothing_when_no_strike_and_ball() { + HintResult hintResult = new HintResult(0, 0); + + assertThat(hintResult.message()).isEqualTo("낫싱"); + } + + @Test + void message_returns_ball_only() { + HintResult hintResult = new HintResult(0, 2); + + assertThat(hintResult.message()).isEqualTo("2볼"); + } + + @Test + void message_returns_strike_only() { + HintResult hintResult = new HintResult(1, 0); + + assertThat(hintResult.message()).isEqualTo("1스트라이크"); + } + + @Test + void message_returns_strike_and_ball_with_space() { + HintResult hintResult = new HintResult(1, 1); + + assertThat(hintResult.message()).isEqualTo("1스트라이크 1볼"); + } + + @Test + void constructor_throws_when_negative_count() { + assertThatThrownBy(() -> new HintResult(-1, 0)) + .isInstanceOf(InvalidHintResultException.class); + } + + @Test + void constructor_throws_when_count_exceeds_three() { + assertThatThrownBy(() -> new HintResult(4, 0)) + .isInstanceOf(InvalidHintResultException.class); + } + + @Test + void constructor_throws_when_total_exceeds_three() { + assertThatThrownBy(() -> new HintResult(2, 2)) + .isInstanceOf(InvalidHintResultException.class); + } +} diff --git a/src/test/java/baseball/domain/models/JudgementTest.java b/src/test/java/baseball/domain/models/JudgementTest.java new file mode 100644 index 00000000..408ceaa3 --- /dev/null +++ b/src/test/java/baseball/domain/models/JudgementTest.java @@ -0,0 +1,41 @@ +package baseball.domain.models; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +class JudgementTest { + @Test + void countStrike_counts_matching_positions() { + List computerNumbers = Arrays.asList(4, 2, 5); + List playerNumbers = Arrays.asList(1, 2, 5); + + Judgement judgement = new Judgement(computerNumbers, playerNumbers); + + assertThat(judgement.countStrike()).isEqualTo(2); + } + + @Test + void countBall_counts_only_numbers_in_different_positions() { + List computerNumbers = Arrays.asList(4, 2, 5); + List playerNumbers = Arrays.asList(4, 5, 6); + + Judgement judgement = new Judgement(computerNumbers, playerNumbers); + + assertThat(judgement.countBall()).isEqualTo(1); + } + + @Test + void countStrike_and_countBall_return_zero_when_no_matches() { + List computerNumbers = Arrays.asList(4, 2, 5); + List playerNumbers = Arrays.asList(1, 3, 6); + + Judgement judgement = new Judgement(computerNumbers, playerNumbers); + + assertThat(judgement.countStrike()).isZero(); + assertThat(judgement.countBall()).isZero(); + } +} diff --git a/src/test/java/baseball/domain/models/PlayerNumbersTest.java b/src/test/java/baseball/domain/models/PlayerNumbersTest.java new file mode 100644 index 00000000..70dc02bb --- /dev/null +++ b/src/test/java/baseball/domain/models/PlayerNumbersTest.java @@ -0,0 +1,45 @@ +package baseball.domain.models; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; + +import baseball.domain.exceptions.InvalidPlayerNumbersException; + +import org.junit.jupiter.api.Test; + +class PlayerNumbersTest { + @Test + void from_parses_three_digits() { + PlayerNumbers playerNumbers = PlayerNumbers.from("123"); + + List numbers = playerNumbers.numbers(); + + assertThat(numbers).containsExactly(1, 2, 3); + } + + @Test + void from_throws_when_length_is_not_three() { + assertThatThrownBy(() -> PlayerNumbers.from("12")) + .isInstanceOf(InvalidPlayerNumbersException.class); + } + + @Test + void from_throws_when_contains_non_digit() { + assertThatThrownBy(() -> PlayerNumbers.from("12a")) + .isInstanceOf(InvalidPlayerNumbersException.class); + } + + @Test + void from_throws_when_contains_zero() { + assertThatThrownBy(() -> PlayerNumbers.from("102")) + .isInstanceOf(InvalidPlayerNumbersException.class); + } + + @Test + void from_throws_when_contains_duplicates() { + assertThatThrownBy(() -> PlayerNumbers.from("112")) + .isInstanceOf(InvalidPlayerNumbersException.class); + } +}