diff --git a/README.md b/README.md
index fcf3f057..bf758ff9 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,40 @@
+## 숫자 야구 게임 기능 목록
+
+#### 게임 컨트롤러 `GameController`
+- [x] 최초에 게임 생성
+- [x] 사용자의 정답 입력에 대해 결과 판정 후 출력
+- [x] 3스트라이크인 경우 해당 게임 종료
+- [x] 게임이 끝난 후 추가 진행 여부 입력이 1인 경우 새 게임 생성
+- [x] 게임이 끝난 후 추가 진행 여부 입력이 2인 경우 전체 게임 종료
+
+#### 숫자 야구 게임 `BaseballGame`
+- [x] 최초 생성시 `BaseballNumbersGenerator`를 통해 정답 생성
+
+#### 컴퓨터 숫자 생성 `BaseballNumbersGenerator`
+- [x] `BaseballNumbers`(1부터 9까지 서로 다른 수로 이루어진 3자리의 수) 생성
+
+#### 사용자의 입력 받음 `InputView`
+- [x] 사용자의 정답을 입력 받음
+- [x] 사용자의 새 게임 진행 여부를 입력 받음
+- [x] 잘못된 입력이 들어오는 경우 에러메시지 출력 후 다시 입력 받음
+
+#### 메시지 출력 `OutputView`
+- [x] `StrikeBallCount`에 대한 결과 출력
+
+#### 사용자의 정답 입력 검증 `InputUserAnswerValidator`
+- [x] 사용자의 정답 입력을 검증
+- [x] 사용자의 정답 입력을 `BaseballNumbers`로 변환
+
+#### 사용자의 새 게임 진행 여부 입력 검증 `InputRestartValidator`
+- [x] 사용자의 새 게임 진행 여부 입력을 검증
+
+#### 결과 판정 `Referee`
+- [x] 게임의 정답과 사용자가 입력한 답을 비교하여 `StrikeBallCount` 생성
+- [x] 각각의 숫자에 대해 포함된 여부, index가 같은 지 확인하여 볼카운트 판정
+
+
+
+
# 숫자 야구 게임
## 진행 방법
* 숫자 야구 게임 요구사항을 파악한다.
@@ -5,3 +42,63 @@
## 과제 제출 과정
* [과제 제출 방법](https://github.com/next-step/nextstep-docs/tree/master/ent-precourse)
+
+
+## 기능 요구 사항
+
+기본적으로 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다.
+
+- 같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 같은 수가 전혀 없으면 포볼 또는 낫싱이란 힌트를 얻고, 그 힌트를 이용해서 먼저 상대방(컴퓨터)의 수를 맞히면 승리한다.
+ - 예시) 상대방(컴퓨터)의 수가 425일 때
+ - 123을 제시한 경우 : 1스트라이크
+ - 456을 제시한 경우 : 1볼 1스트라이크
+ - 789를 제시한 경우 : 낫싱
+- 위 숫자 야구 게임에서 상대방의 역할을 컴퓨터가 한다. 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택한다. 게임 플레이어는 컴퓨터가 생각하고 있는 서로 다른 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한
+ 결과를 출력한다.
+- 이 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임이 종료된다.
+- 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다.
+
+#### 실행 결과 예시
+
+```
+숫자를 입력해주세요 : 123
+1스트라이크 1볼
+숫자를 입력해주세요 : 145
+1볼
+숫자를 입력해주세요 : 671
+2볼
+숫자를 입력해주세요 : 216
+1스트라이크
+숫자를 입력해주세요 : 713
+3스트라이크
+3개의 숫자를 모두 맞히셨습니다! 게임 종료
+게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.
+1
+숫자를 입력해주세요 : 123
+1스트라이크 1볼
+...
+```
+
+---
+
+## 프로그래밍 요구 사항 - 메소드 분리
+
+- 자바 코드 컨벤션을 지켜가며 프로그래밍한다.
+ - https://github.com/woowacourse/woowacourse-docs/tree/master/styleguide/java 참고
+- indent(인덴트, 들여쓰기) depth를 2가 넘지 않도록 구현한다. 1까지만 허용한다.
+ - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
+ - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
+- 자바 8에 추가된 stream api를 사용하지 않고 구현해야 한다. 단, 람다는 사용 가능하다.
+- else 예약어를 쓰지 않는다.
+ - 힌트 : if 절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
+ - else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데, switch/case도 허용하지 않는다.
+- 함수(또는 메소드)의 길이가 10라인을 넘어가지 않도록 구현한다.
+ - 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.
+
+## 프로그래밍 요구 사항 - 단위 테스트
+
+- 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in, Scanner) 로직은 제외
+ - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.
+
+- JUni5와 AssertJ 사용법에 익숙하지 않은 개발자는 첨부한 "학습테스트를 통해 JUnit 학습하기.pdf" 문서를 참고해 사용법을 익힌 후 JUnit5 기반 단위 테스트를 구현한다.
+
diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java
new file mode 100644
index 00000000..157994c7
--- /dev/null
+++ b/src/main/java/baseball/Application.java
@@ -0,0 +1,14 @@
+package baseball;
+
+import baseball.controller.GameController;
+import baseball.view.InputView;
+import baseball.view.OutputView;
+
+public class Application {
+ public static void main(String[] args) {
+ InputView inputView = new InputView();
+ OutputView outputView = new OutputView();
+ GameController gameController = new GameController(inputView, outputView);
+ gameController.play();
+ }
+}
diff --git a/src/main/java/baseball/BaseballNumbersGenerator.java b/src/main/java/baseball/BaseballNumbersGenerator.java
new file mode 100644
index 00000000..7fcec62c
--- /dev/null
+++ b/src/main/java/baseball/BaseballNumbersGenerator.java
@@ -0,0 +1,25 @@
+package baseball;
+
+import baseball.constant.Constant;
+import baseball.domain.BaseballNumbers;
+import java.util.Random;
+
+public class BaseballNumbersGenerator {
+ Random random;
+ private final int MIN = Constant.MIN_RANGE.getValue();
+ private final int MAX = Constant.MAX_RANGE.getValue();
+
+ public BaseballNumbersGenerator() {
+ random = new Random();
+ }
+
+ public BaseballNumbers generate() {
+ BaseballNumbers baseballNumbers = new BaseballNumbers();
+ boolean isFull = false;
+ while (!isFull) {
+ int nextNumber = MIN + random.nextInt(MAX - MIN);
+ isFull = baseballNumbers.add(nextNumber);
+ }
+ return baseballNumbers;
+ }
+}
diff --git a/src/main/java/baseball/constant/Command.java b/src/main/java/baseball/constant/Command.java
new file mode 100644
index 00000000..b9adf048
--- /dev/null
+++ b/src/main/java/baseball/constant/Command.java
@@ -0,0 +1,15 @@
+package baseball.constant;
+
+public enum Command {
+ RESTART("1"), QUIT("2");
+
+ private final String value;
+
+ Command(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/src/main/java/baseball/constant/Constant.java b/src/main/java/baseball/constant/Constant.java
new file mode 100644
index 00000000..04fad46e
--- /dev/null
+++ b/src/main/java/baseball/constant/Constant.java
@@ -0,0 +1,15 @@
+package baseball.constant;
+
+public enum Constant {
+ LENGTH(3), MIN_RANGE(1), MAX_RANGE(9);
+
+ private final int value;
+
+ Constant(int number) {
+ this.value = number;
+ }
+
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/src/main/java/baseball/controller/GameController.java b/src/main/java/baseball/controller/GameController.java
new file mode 100644
index 00000000..29f72a91
--- /dev/null
+++ b/src/main/java/baseball/controller/GameController.java
@@ -0,0 +1,47 @@
+package baseball.controller;
+
+import baseball.BaseballNumbersGenerator;
+import baseball.constant.Constant;
+import baseball.domain.BaseballGame;
+import baseball.domain.StrikeBallCount;
+import baseball.view.InputView;
+import baseball.view.OutputView;
+
+public class GameController {
+ private static final String END_FORMAT = "%d개의 숫자를 모두 맞히셨습니다! 게임 종료";
+ private static final String END_MESSAGE = String.format(END_FORMAT, Constant.LENGTH.getValue());
+ private final InputView inputView;
+ private final OutputView outputView;
+ private final BaseballNumbersGenerator baseballNumbersGenerator;
+ private BaseballGame baseballGame;
+ private StrikeBallCount strikeBallCount;
+
+ public GameController(InputView inputView, OutputView outputView) {
+ this.inputView = inputView;
+ this.outputView = outputView;
+ baseballNumbersGenerator = new BaseballNumbersGenerator();
+ }
+
+ public void play() {
+ do {
+ initializeGame();
+ playGame();
+ System.out.println(END_MESSAGE);
+ } while (inputView.readInputRestart());
+ }
+
+ private void initializeGame() {
+ baseballGame = new BaseballGame(baseballNumbersGenerator.generate());
+ }
+
+ private void playGame() {
+ do {
+ strikeBallCount = baseballGame.getResult(inputView.readInputUserAnswer());
+ printResult();
+ } while (!strikeBallCount.getIsComplete());
+ }
+
+ private void printResult() {
+ outputView.printResult(strikeBallCount);
+ }
+}
diff --git a/src/main/java/baseball/domain/BaseballGame.java b/src/main/java/baseball/domain/BaseballGame.java
new file mode 100644
index 00000000..22b79861
--- /dev/null
+++ b/src/main/java/baseball/domain/BaseballGame.java
@@ -0,0 +1,13 @@
+package baseball.domain;
+
+public class BaseballGame {
+ private final Referee referee;
+
+ public BaseballGame(BaseballNumbers baseballNumbers) {
+ referee = new Referee(baseballNumbers);
+ }
+
+ public StrikeBallCount getResult(BaseballNumbers userNumbers) {
+ return referee.judge(userNumbers);
+ }
+}
diff --git a/src/main/java/baseball/domain/BaseballNumbers.java b/src/main/java/baseball/domain/BaseballNumbers.java
new file mode 100644
index 00000000..2383985d
--- /dev/null
+++ b/src/main/java/baseball/domain/BaseballNumbers.java
@@ -0,0 +1,52 @@
+package baseball.domain;
+
+import baseball.constant.Constant;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BaseballNumbers {
+ private final List numbers;
+ private final int length = Constant.LENGTH.getValue();
+
+ public BaseballNumbers() {
+ numbers = new ArrayList<>();
+ }
+
+ public BaseballNumbers(String[] input) {
+ this();
+ for (String num : input) {
+ this.add(Integer.parseInt(num));
+ }
+ }
+
+ public boolean contains(int number) {
+ return numbers.contains(number);
+ }
+
+ public boolean add(int number) {
+ if (!contains(number)) {
+ numbers.add(number);
+ }
+ return isFull();
+ }
+
+ public boolean isFull() {
+ return numbers.size() == length;
+ }
+
+ public int getNumberAt(int index) {
+ return numbers.get(index);
+ }
+
+ public int getIndex(int number) {
+ return numbers.indexOf(number);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof BaseballNumbers) {
+ return numbers.equals(((BaseballNumbers) obj).numbers);
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/baseball/domain/Referee.java b/src/main/java/baseball/domain/Referee.java
new file mode 100644
index 00000000..58d388f9
--- /dev/null
+++ b/src/main/java/baseball/domain/Referee.java
@@ -0,0 +1,37 @@
+package baseball.domain;
+
+import baseball.constant.Constant;
+
+public class Referee {
+ private final BaseballNumbers answer;
+ private final StrikeBallCount strikeBallCount;
+ private final int length = Constant.LENGTH.getValue();
+
+ public Referee(BaseballNumbers answer) {
+ this.answer = answer;
+ this.strikeBallCount = new StrikeBallCount();
+ }
+
+ public StrikeBallCount judge(BaseballNumbers userNumbers) {
+ strikeBallCount.resetCounts();
+ for (int index = 0; index < length; index++) {
+ int number = userNumbers.getNumberAt(index);
+ judgeOneNumber(index, number);
+ }
+ return strikeBallCount;
+ }
+
+ private void judgeOneNumber(int index, int number) {
+ if (answer.contains(number)) {
+ changeCount(index, number);
+ }
+ }
+
+ private void changeCount(int index, int number) {
+ if (index == answer.getIndex(number)) {
+ strikeBallCount.addStrikeCount();
+ return;
+ }
+ strikeBallCount.addBallCount();
+ }
+}
diff --git a/src/main/java/baseball/domain/StrikeBallCount.java b/src/main/java/baseball/domain/StrikeBallCount.java
new file mode 100644
index 00000000..5c8c0031
--- /dev/null
+++ b/src/main/java/baseball/domain/StrikeBallCount.java
@@ -0,0 +1,47 @@
+package baseball.domain;
+
+import baseball.constant.Constant;
+
+public class StrikeBallCount {
+ private int strikeCount;
+ private int ballCount;
+ private final int length;
+
+ public StrikeBallCount() {
+ length = Constant.LENGTH.getValue();
+ }
+
+ public void resetCounts() {
+ strikeCount = 0;
+ ballCount = 0;
+ }
+
+ public void addStrikeCount() {
+ strikeCount++;
+ }
+
+ public void addBallCount() {
+ ballCount++;
+ }
+
+ public int getStrikeCount() {
+ return strikeCount;
+ }
+
+ public int getBallCount() {
+ return ballCount;
+ }
+
+ public boolean getIsComplete() {
+ return strikeCount == length;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof StrikeBallCount) {
+ StrikeBallCount strikeBallCount = (StrikeBallCount) obj;
+ return strikeCount == strikeBallCount.getStrikeCount() && ballCount == strikeBallCount.getBallCount();
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/baseball/validator/InputRestartValidator.java b/src/main/java/baseball/validator/InputRestartValidator.java
new file mode 100644
index 00000000..f05f2468
--- /dev/null
+++ b/src/main/java/baseball/validator/InputRestartValidator.java
@@ -0,0 +1,20 @@
+package baseball.validator;
+
+import baseball.constant.Command;
+
+public class InputRestartValidator extends Validator {
+ private static final String restart = Command.RESTART.getValue();
+ private static final String quit = Command.QUIT.getValue();
+ private static final String INVALID_RESTART_INPUT_ERROR_MESSAGE = "[ERROR] 입력을 확인해주세요.";
+
+ public static Boolean validate(String input) {
+ String trimmedInput = removeSpace(input);
+ if (trimmedInput.equals(restart)) {
+ return true;
+ }
+ if (trimmedInput.equals(quit)) {
+ return false;
+ }
+ throw new IllegalArgumentException(INVALID_RESTART_INPUT_ERROR_MESSAGE);
+ }
+}
diff --git a/src/main/java/baseball/validator/InputUserAnswerValidator.java b/src/main/java/baseball/validator/InputUserAnswerValidator.java
new file mode 100644
index 00000000..ec79a0be
--- /dev/null
+++ b/src/main/java/baseball/validator/InputUserAnswerValidator.java
@@ -0,0 +1,32 @@
+package baseball.validator;
+
+import baseball.constant.Constant;
+import baseball.domain.BaseballNumbers;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.regex.Pattern;
+
+public class InputUserAnswerValidator extends Validator {
+ private static final int MIN = Constant.MIN_RANGE.getValue();
+ private static final int MAX = Constant.MAX_RANGE.getValue();
+ private static final int LENGTH = Constant.LENGTH.getValue();
+ private static final String REGEX_ERROR_MESSAGE_FORMAT = "[ERROR] 형식에 맞게 입력하세요 : 중복이 없는 %d과 %d 사이의 %d자리 수";
+ private static final String DUPLICATE_ERROR_MESSAGE = "[ERROR] 중복이 없어야 합니다.";
+
+ private static final String ANSWER_REGEX = String.format("^[%d-%d]{%d}$", MIN, MAX, LENGTH);
+
+ public static BaseballNumbers validate(String inputAnswer) {
+ String trimmedInput = removeSpace(inputAnswer);
+ if (Pattern.matches(ANSWER_REGEX, trimmedInput)) {
+ return validateUniqueness(trimmedInput);
+ }
+ throw new IllegalArgumentException(String.format(REGEX_ERROR_MESSAGE_FORMAT, MIN, MAX, LENGTH));
+ }
+
+ public static BaseballNumbers validateUniqueness(String input) {
+ if (new HashSet<>(Arrays.asList(input.split(""))).size() == input.length()) {
+ return new BaseballNumbers(input.split(""));
+ }
+ throw new IllegalArgumentException(DUPLICATE_ERROR_MESSAGE);
+ }
+}
diff --git a/src/main/java/baseball/validator/Validator.java b/src/main/java/baseball/validator/Validator.java
new file mode 100644
index 00000000..ef9a09cd
--- /dev/null
+++ b/src/main/java/baseball/validator/Validator.java
@@ -0,0 +1,11 @@
+package baseball.validator;
+
+public abstract class Validator {
+ static Object validate(String input) throws IllegalArgumentException {
+ return null;
+ }
+
+ static String removeSpace(String input) {
+ return input.replaceAll(" ", "");
+ }
+}
diff --git a/src/main/java/baseball/view/InputView.java b/src/main/java/baseball/view/InputView.java
new file mode 100644
index 00000000..933901d2
--- /dev/null
+++ b/src/main/java/baseball/view/InputView.java
@@ -0,0 +1,40 @@
+package baseball.view;
+
+import baseball.constant.Command;
+import baseball.domain.BaseballNumbers;
+import baseball.validator.InputRestartValidator;
+import baseball.validator.InputUserAnswerValidator;
+
+import java.util.Scanner;
+
+public class InputView {
+ private final String INPUT_ANSWER_MESSAGE = "숫자를 입력해주세요 : ";
+ private final String INPUT_RESTART_MESSAGE_FORMAT = "게임을 새로 시작하려면 %s, 종료하려면 %s를 입력하세요.";
+ private final String INPUT_RESTART_MESSAGE = String.format(INPUT_RESTART_MESSAGE_FORMAT,
+ Command.RESTART.getValue(), Command.QUIT.getValue());
+ private final Scanner sc;
+
+ public InputView() {
+ sc = new Scanner(System.in);
+ }
+
+ public BaseballNumbers readInputUserAnswer() {
+ System.out.print(INPUT_ANSWER_MESSAGE);
+ try {
+ return InputUserAnswerValidator.validate(sc.next());
+ } catch (IllegalArgumentException e) {
+ System.out.println(e.getMessage());
+ return readInputUserAnswer();
+ }
+ }
+
+ public boolean readInputRestart() {
+ System.out.println(INPUT_RESTART_MESSAGE);
+ try {
+ return InputRestartValidator.validate(sc.next());
+ } catch (IllegalArgumentException e) {
+ System.out.println(e.getMessage());
+ return readInputRestart();
+ }
+ }
+}
diff --git a/src/main/java/baseball/view/OutputView.java b/src/main/java/baseball/view/OutputView.java
new file mode 100644
index 00000000..e18760b7
--- /dev/null
+++ b/src/main/java/baseball/view/OutputView.java
@@ -0,0 +1,27 @@
+package baseball.view;
+
+import baseball.domain.StrikeBallCount;
+
+public class OutputView {
+ private final String STRIKE_KOR = "스트라이크 ";
+ private final String BALL_KOR = "볼";
+ private final String NOTHING_KOR = "낫싱";
+ private StringBuilder sb;
+
+ public void printResult(StrikeBallCount strikeBallCount) {
+ sb = new StringBuilder();
+ addString(strikeBallCount.getStrikeCount(), STRIKE_KOR);
+ addString(strikeBallCount.getBallCount(), BALL_KOR);
+ if (sb.length() == 0) {
+ sb.append(NOTHING_KOR);
+ }
+ System.out.println(sb.toString().trim());
+ }
+
+ public void addString(int num, String kor_string) {
+ if (num > 0) {
+ sb.append(num);
+ sb.append(kor_string);
+ }
+ }
+}
diff --git a/src/test/java/baseball/BaseballGameTest.java b/src/test/java/baseball/BaseballGameTest.java
new file mode 100644
index 00000000..b2122713
--- /dev/null
+++ b/src/test/java/baseball/BaseballGameTest.java
@@ -0,0 +1,43 @@
+package baseball;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import baseball.domain.BaseballGame;
+import baseball.domain.BaseballNumbers;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+public class BaseballGameTest {
+ BaseballGame baseballGame = new BaseballGame(createBaseballNumbers("123"));
+
+ @DisplayName("inputUserAnswer가 1~9로 이루어진 세 자리의 수인 경우 BaseballNumbers를 정상적으로 반환한다.")
+ @Test
+ void BaseballGame_Judge_테스트() {
+ assertThat(baseballGame.getResult(createBaseballNumbers("456")))
+ .isEqualTo(StrikeBallCountCombination.count_00.getStrikeBallCount());
+ assertThat(baseballGame.getResult(createBaseballNumbers("436")))
+ .isEqualTo(StrikeBallCountCombination.count_01.getStrikeBallCount());
+ assertThat(baseballGame.getResult(createBaseballNumbers("237")))
+ .isEqualTo(StrikeBallCountCombination.count_02.getStrikeBallCount());
+ assertThat(baseballGame.getResult(createBaseballNumbers("312")))
+ .isEqualTo(StrikeBallCountCombination.count_03.getStrikeBallCount());
+ assertThat(baseballGame.getResult(createBaseballNumbers("156")))
+ .isEqualTo(StrikeBallCountCombination.count_10.getStrikeBallCount());
+ assertThat(baseballGame.getResult(createBaseballNumbers("134")))
+ .isEqualTo(StrikeBallCountCombination.count_11.getStrikeBallCount());
+ assertThat(baseballGame.getResult(createBaseballNumbers("132")))
+ .isEqualTo(StrikeBallCountCombination.count_12.getStrikeBallCount());
+ assertThat(baseballGame.getResult(createBaseballNumbers("124")))
+ .isEqualTo(StrikeBallCountCombination.count_20.getStrikeBallCount());
+ //assertThat(baseballGame.getResult(createBaseballNumbers("124")))
+ // .isEqualTo(StrikeBallCountCombination.count_21.getStrikeBallCount());
+ //2스트라이크 1볼은 불가능
+ assertThat(baseballGame.getResult(createBaseballNumbers("123")))
+ .isEqualTo(StrikeBallCountCombination.count_30.getStrikeBallCount());
+ }
+
+ private BaseballNumbers createBaseballNumbers(String numbers) {
+ return new BaseballNumbers(numbers.split(""));
+ }
+}
+
diff --git a/src/test/java/baseball/InputRestartValidatorTest.java b/src/test/java/baseball/InputRestartValidatorTest.java
new file mode 100644
index 00000000..2156251f
--- /dev/null
+++ b/src/test/java/baseball/InputRestartValidatorTest.java
@@ -0,0 +1,33 @@
+package baseball;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import baseball.validator.InputRestartValidator;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+public class InputRestartValidatorTest {
+ private static final String INVALID_RESTART_INPUT_ERROR_MESSAGE = "[ERROR] 입력을 확인해주세요.";
+
+ @DisplayName("재시작 입력이 '1' + 공백인 경우 true, '2'인 경우 false를 return한다.")
+ @Test
+ void InputRestart_성공테스트() {
+ assertThat(InputRestartValidator.validate("1")).isEqualTo(true);
+ assertThat(InputRestartValidator.validate("1 ")).isEqualTo(true);
+ assertThat(InputRestartValidator.validate(" 1 ")).isEqualTo(true);
+ assertThat(InputRestartValidator.validate("2")).isEqualTo(false);
+ }
+
+ @DisplayName("재시작 입력이 1, 2가 아닌 경우 예외가 발생한다.")
+ @ValueSource(strings = {"a", "09", "c", "d"})
+ @ParameterizedTest
+ void InputRestart_실패테스트(String inputString) {
+ assertThatThrownBy(() -> InputRestartValidator.validate(inputString))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(INVALID_RESTART_INPUT_ERROR_MESSAGE);
+ }
+}
+
diff --git a/src/test/java/baseball/InputUserAnswerValidatorTest.java b/src/test/java/baseball/InputUserAnswerValidatorTest.java
new file mode 100644
index 00000000..b06df68e
--- /dev/null
+++ b/src/test/java/baseball/InputUserAnswerValidatorTest.java
@@ -0,0 +1,46 @@
+package baseball;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import baseball.domain.BaseballNumbers;
+import baseball.validator.InputUserAnswerValidator;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+public class InputUserAnswerValidatorTest {
+ private static final String REGEX_ERROR_MESSAGE = "[ERROR] 형식에 맞게 입력하세요 : 중복이 없는 1과 9 사이의 3자리 수";
+ private static final String DUPLICATE_ERROR_MESSAGE = "[ERROR] 중복이 없어야 합니다.";
+
+ @DisplayName("inputUserAnswer가 1~9로 이루어진 세 자리의 수인 경우 BaseballNumbers를 정상적으로 반환한다.")
+ @Test
+ void InputUserAnswer_성공테스트() {
+ assertThat(InputUserAnswerValidator.validate("123"))
+ .isEqualTo(new BaseballNumbers(new String[]{"1", "2", "3"}));
+ assertThat(InputUserAnswerValidator.validate("1 2 3 "))
+ .isEqualTo(new BaseballNumbers(new String[]{"1", "2", "3"}));
+ assertThat(InputUserAnswerValidator.validate(" 7 89"))
+ .isEqualTo(new BaseballNumbers(new String[]{"7", "8", "9"}));
+ }
+
+ @DisplayName("inputUserAnswer가 1~9로 이루어진 세 자리의 수가 아닌 경우 예외가 발생한다.")
+ @ValueSource(strings = {"aab", "세자리", "1234", "53129", "-1", "dㅁㅇㅎ", "100"})
+ @ParameterizedTest
+ void InputUserAnswer_형식_실패테스트(String inputString) {
+ assertThatThrownBy(() -> InputUserAnswerValidator.validate(inputString))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(REGEX_ERROR_MESSAGE);
+ }
+
+ @DisplayName("inputUserAnswer가 1~9로 이루어진 세 자리의 수지만 중복이 있는 경우 예외가 발생한다.")
+ @ValueSource(strings = {"113", "121", "122", "111", "1 13", "1 2 1", " 1 2 2 "})
+ @ParameterizedTest
+ void InputUserAnswer_중복_실패테스트(String inputString) {
+ assertThatThrownBy(() -> InputUserAnswerValidator.validate(inputString))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(DUPLICATE_ERROR_MESSAGE);
+ }
+}
+
diff --git a/src/test/java/baseball/OutputViewTest.java b/src/test/java/baseball/OutputViewTest.java
new file mode 100644
index 00000000..54fcd3bd
--- /dev/null
+++ b/src/test/java/baseball/OutputViewTest.java
@@ -0,0 +1,39 @@
+package baseball;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import baseball.domain.StrikeBallCount;
+import baseball.view.OutputView;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+public class OutputViewTest {
+ OutputView outputView = new OutputView();
+ private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ @BeforeEach
+ public void setOutStreams() {
+ System.setOut(new PrintStream(outputStream));
+ }
+
+ @AfterEach
+ public void restoreStreams() {
+ System.setOut(System.out);
+ outputStream.reset();
+ }
+
+ @DisplayName("모든 StrikeBallCount에 대해 제대로 출력하는지 확인한다.")
+ @ParameterizedTest
+ @EnumSource(StrikeBallCountCombination.class)
+ void printResultTest(StrikeBallCountCombination countCombination) {
+ StrikeBallCount sb = countCombination.getStrikeBallCount();
+ outputView.printResult(sb);
+ assertThat(outputStream.toString().trim()).isEqualTo(countCombination.result);
+ }
+}
+
diff --git a/src/test/java/baseball/StrikeBallCountCombination.java b/src/test/java/baseball/StrikeBallCountCombination.java
new file mode 100644
index 00000000..307cf119
--- /dev/null
+++ b/src/test/java/baseball/StrikeBallCountCombination.java
@@ -0,0 +1,42 @@
+package baseball;
+
+import baseball.domain.StrikeBallCount;
+
+public enum StrikeBallCountCombination {
+ count_00(0, 0, "낫싱"),
+ count_01(0, 1, "1볼"),
+ count_02(0, 2, "2볼"),
+ count_03(0, 3, "3볼"),
+ count_10(1, 0, "1스트라이크"),
+ count_11(1, 1, "1스트라이크 1볼"),
+ count_12(1, 2, "1스트라이크 2볼"),
+ count_20(2, 0, "2스트라이크"),
+ count_21(2, 1, "2스트라이크 1볼"),
+ count_30(3, 0, "3스트라이크");
+ public final int strikes;
+ public final int balls;
+ public final String result;
+ StrikeBallCount sb;
+
+ StrikeBallCountCombination(int strikes, int balls, String result) {
+ this.strikes = strikes;
+ this.balls = balls;
+ this.result = result;
+ sb = new StrikeBallCount();
+ setStrikeBallCount();
+ }
+
+ private void setStrikeBallCount() {
+ sb.resetCounts();
+ for (int i = 0; i < strikes; i++) {
+ sb.addStrikeCount();
+ }
+ for (int i = 0; i < balls; i++) {
+ sb.addBallCount();
+ }
+ }
+
+ public StrikeBallCount getStrikeBallCount() {
+ return sb;
+ }
+}
diff --git a/src/test/java/study/StringTest.java b/src/test/java/study/StringTest.java
deleted file mode 100644
index 43e47d90..00000000
--- a/src/test/java/study/StringTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package study;
-
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class StringTest {
- @Test
- void replace() {
- String actual = "abc".replace("b", "d");
- assertThat(actual).isEqualTo("adc");
- }
-}