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"); - } -}