diff --git a/README.md b/README.md index 8ae5f84918..2cb2f10ac3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,98 @@ # 숫자 야구 게임 -## 진행 방법 -* 숫자 야구 게임 요구사항을 파악한다. -* 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 과제를 제출한다. +## 진행 방식 +* 미션은 과제 진행 요구 사항, 기능 요구 사항, 프로그래밍 요구 사항 세 가지로 구성되어 있다. +* 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. +* 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다. -## 과제 제출 과정 -* [과제 제출 방법](https://github.com/next-step/nextstep-docs/tree/master/precourse) +## 구현할 기능 목록 +* [x] 야구 게임에 필요한 입출력 처리 +* [x] 야구 게임 진행 (+ 게임 재게 기능) +* [x] 야구 게임의 룰 정의 +* [x] 테스트 코드 작성 + + +## 미션 제출 방법 + +* [x] 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다. + * [x] GitHub을 활용한 제출 방법은 미니과제 진행 가이드 문서를 참고해 제출한다. + * [x] 풀 리퀘스트 제목은 '[OO대 트랙_본명] 미션 제출합니다.'로 작성한다. +* [x] GitHub에 미션을 제출한 후 구글 폼에 PR 링크를 포함하여 최종 제출한다. + +## 과제 제출 전 체크 리스트 + +* [x] 터미널에서 `java -version` 을 실행하여 Java 버전이 17인지 확인한다. Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 17로 실행되는지 확인한다. +* [x] 터미널에서 Mac 또는 Linux 사용자의 경우 명령을 실행하고, Windows 사용자의 경우 또는 명령을 실행할 때 모든 테스트가 아래와 같이 통과하는지 확인한다. +``` +BUILD SUCCESSFUL in 0s +``` + +## 과제 진행 요구 사항 + +* [x] 미션은 숫자 야구 저장소를 포크하고 클론하는 것으로 시작한다. +* [x] 기능을 구현하기 전 README.md 에 구현할 기능 목록을 정리해 추가한다. +* [x] Git의 커밋 단위는 앞 단계에서 README.md 에 정리한 기능 목록 단위로 추가한다. + * [x] AngularJS Git Commit Message Conventions을 참고해 커밋 메시지를 작성한다. +* [x] 자세한 과제 진행 방법은 미니과제 진행 가이드 문서를 참고한다 + +## 기능 요구 사항 +* [x] 기본적으로 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다 + +* [x] 같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 같은 수가 전혀 없으면 낫싱이란 힌트를 얻고, 그 힌트를 이용해서 먼저 상대방(컴퓨터)의 + 수를 맞추면 승리한다. +~~~ +e.g. +상대방(컴퓨터)의 수가 425일 때, +- 123을 제시한 경우: 1스트라이크 +- 456을 제시한 경우: 1볼 1스트라이크 +- 789를 제시한 경우: 낫싱 +~~~ +* [x] 위 숫자 야구 게임에서 상대방의 역할을 컴퓨터가 한다. 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택한다. 게임 플레이어는 컴퓨터가 생각하고 있는 +3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한 결과를 출력한다. +* [x] 이 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임이 종료된다. +* [x] 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다. +* [x] 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException` 을 발생시킨 후 애플리케이션은 종료되어야 한다. + + +## 실행 결과 +~~~ +숫자를 입력해 주세요 : 123 +1볼 1스트라이크 +숫자를 입력해 주세요 : 145 +1볼 +숫자를 입력해 주세요 : 671 +2볼 +숫자를 입력해 주세요 : 216 +1스트라이크 +숫자를 입력해 주세요 : 713 +3스트라이크 +3개의 숫자를 모두 맞히셨습니다! 게임 종료 +게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요. +1 +숫자를 입력해 주세요 : 123 +1볼 +… +~~~ + +## 프로그래밍 요구 사항 1 +* [x] JDK 17 버전에서 실행 가능해야 한다. +* [x] 프로그램 실행의 시작점은 kim.half.Application 의 main() 이다. +* [x] build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다. +* [x] 프로그램 종료 시 System.exit() 를 호출하지 않는다. +* [x] 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. + +## 프로그래밍 요구 사항 2 + +* [x] 자바 코드 컨벤션을 지키면서 프로그래밍한다. + * [x] 기본적으로 Google Java Style Guide을 원칙으로 한다. + * [x] 단, 들여쓰기는 '2 spaces'가 아닌 '4 spaces'로 한다. +* [x] indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. + * 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. + * 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. +* [x] 3항 연산자를 쓰지 않는다. +* [x] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. +* [x] JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. +* 테스트 도구 사용법이 익숙하지 않다면 아래 문서를 참고하여 학습한 후 테스트를 구현한다. + * JUnit 5 User Guide + * AssertJ User Guide + * AssertJ Exception Assertions + * Guide to JUnit 5 Parameterized Tests \ No newline at end of file diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/kim/half/Application.java b/src/main/java/kim/half/Application.java new file mode 100644 index 0000000000..cd0e7eadb6 --- /dev/null +++ b/src/main/java/kim/half/Application.java @@ -0,0 +1,11 @@ +package kim.half; + +import kim.half.service.BaseballGameManager; + +public class Application { + + public static void main(String[] args) { + BaseballGameManager gameManager = new BaseballGameManager(); + gameManager.start(); + } +} diff --git a/src/main/java/kim/half/domain/Baseball.java b/src/main/java/kim/half/domain/Baseball.java new file mode 100644 index 0000000000..b417f6ca6f --- /dev/null +++ b/src/main/java/kim/half/domain/Baseball.java @@ -0,0 +1,46 @@ +package kim.half.domain; + +import static kim.half.ui.OutputBuilder.INPUT_NUMBER_FORMAT_EXCEPTION_MESSAGE; + +import java.util.Objects; + +public class Baseball { + + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 9; + + private final int baseballNumber; + + public Baseball(int baseballNumber) { + isValidBaseballNumber(baseballNumber); + + this.baseballNumber = baseballNumber; + } + + public int getNumber() { + return baseballNumber; + } + + private void isValidBaseballNumber(int baseballNumber) { + if (baseballNumber < MIN_NUMBER || baseballNumber > MAX_NUMBER) { + throw new IllegalArgumentException(INPUT_NUMBER_FORMAT_EXCEPTION_MESSAGE); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Baseball baseball = (Baseball) o; + return baseballNumber == baseball.baseballNumber; + } + + @Override + public int hashCode() { + return Objects.hash(baseballNumber); + } +} diff --git a/src/main/java/kim/half/domain/BaseballFactory.java b/src/main/java/kim/half/domain/BaseballFactory.java new file mode 100644 index 0000000000..1e59c5305f --- /dev/null +++ b/src/main/java/kim/half/domain/BaseballFactory.java @@ -0,0 +1,40 @@ +package kim.half.domain; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class BaseballFactory { + + private static final char ZERO_CHAR = '0'; + private static final int VALID_BASEBALLS_SIZE = 3; + private static final int RANDOM_NUMBER_ORIGIN = 1; + private static final int RANDOM_NUMBER_BOUND = 10; + + public static Baseballs generateRandomBaseballs() { + return BaseballFactory.createRandomBaseballs(); + } + + public static Baseballs createBaseballs(String baseballNumbers) { + List baseballs = new ArrayList<>(); + + for (int i = 0; i < baseballNumbers.length(); i++) { + baseballs.add(createBaseball(baseballNumbers.charAt(i) - ZERO_CHAR)); + } + return new Baseballs(baseballs); + } + + public static Baseballs createRandomBaseballs() { + List baseballs = new ArrayList<>(); + + new Random().ints(RANDOM_NUMBER_ORIGIN, RANDOM_NUMBER_BOUND) + .distinct() + .limit(VALID_BASEBALLS_SIZE) + .forEach(randomNumber -> baseballs.add(createBaseball(randomNumber))); + return new Baseballs(baseballs); + } + + private static Baseball createBaseball(int number) { + return new Baseball(number); + } +} diff --git a/src/main/java/kim/half/domain/Baseballs.java b/src/main/java/kim/half/domain/Baseballs.java new file mode 100644 index 0000000000..2a60a53fc9 --- /dev/null +++ b/src/main/java/kim/half/domain/Baseballs.java @@ -0,0 +1,71 @@ +package kim.half.domain; + +import java.util.List; +import java.util.Objects; +import java.util.stream.IntStream; +import kim.half.ui.OutputBuilder; + +public class Baseballs { + + private static final int VALID_BASEBALLS_SIZE = 3; + private static final int ZERO_START_INDEX = 0; + private static final int EXIST_NUMBER = 1; + + private final List baseballs; + + public Baseballs(List baseballs) { + isValidSize(baseballs); + + this.baseballs = baseballs; + } + + public int size() { + return this.baseballs.size(); + } + + public Baseball get(int index) { + return this.baseballs.get(index); + } + + private void isValidSize(List baseballs) { + if (baseballs.size() != VALID_BASEBALLS_SIZE) { + throw new IllegalArgumentException(OutputBuilder.BASEBALLS_SIZE_EXCEPTION_MESSAGE); + } + } + + + public int countStrike(Baseballs cmpBaseballs) { + return (int) IntStream.range(ZERO_START_INDEX, VALID_BASEBALLS_SIZE) + .filter(i -> baseballs.get(i).equals(cmpBaseballs.baseballs.get(i))) + .count(); + } + + public int countBall(Baseballs cmpBaseballs) { + return (int) IntStream.range(ZERO_START_INDEX, VALID_BASEBALLS_SIZE) + .filter(i -> !baseballs.get(i).equals(cmpBaseballs.baseballs.get(i)) && + EXIST_NUMBER == cmpBaseballs.baseballs.stream() + .filter(baseball -> baseball.equals(this.baseballs.get(i))) + .count()) + .count(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Baseballs baseballs = (Baseballs) o; + return VALID_BASEBALLS_SIZE == + IntStream.range(ZERO_START_INDEX, VALID_BASEBALLS_SIZE) + .filter(i -> this.baseballs.get(i).equals(baseballs.baseballs.get(i))) + .count(); + } + + @Override + public int hashCode() { + return Objects.hash(baseballs); + } +} \ No newline at end of file diff --git a/src/main/java/kim/half/service/BaseballGame.java b/src/main/java/kim/half/service/BaseballGame.java new file mode 100644 index 0000000000..7429de40d7 --- /dev/null +++ b/src/main/java/kim/half/service/BaseballGame.java @@ -0,0 +1,35 @@ +package kim.half.service; + +import kim.half.domain.BaseballFactory; +import kim.half.domain.Baseballs; +import kim.half.ui.InputScanner; +import kim.half.ui.OutputBuilder; + +public class BaseballGame { + + private Baseballs userBaseballs; + private Baseballs answerBaseballs; + + public void playBaseballGame() { + answerBaseballs = BaseballFactory.generateRandomBaseballs(); + do { + playBaseballOneRound(); + } while (!checkAnswer()); + OutputBuilder.printCorrectAnswer(); + } + + private void playBaseballOneRound() { + userBaseballs = receiveUserBaseballs(); + BaseballGameResult result = new BaseballGameResult(answerBaseballs, userBaseballs); + + OutputBuilder.printBaseballGameResult(result.getResult()); + } + + private Baseballs receiveUserBaseballs() { + return BaseballFactory.createBaseballs(InputScanner.scanInputNumbers()); + } + + private boolean checkAnswer() { + return userBaseballs.equals(answerBaseballs); + } +} diff --git a/src/main/java/kim/half/service/BaseballGameManager.java b/src/main/java/kim/half/service/BaseballGameManager.java new file mode 100644 index 0000000000..5196edf88b --- /dev/null +++ b/src/main/java/kim/half/service/BaseballGameManager.java @@ -0,0 +1,18 @@ +package kim.half.service; + +import kim.half.ui.InputScanner; + +public class BaseballGameManager { + + public void start() { + do { + BaseballGame baseballGame = new BaseballGame(); + baseballGame.playBaseballGame(); + } while (checkResumeCondition()); + } + + private boolean checkResumeCondition() { + ResumeChecker opinion = new ResumeChecker(InputScanner.scanResumeChoice()); + return opinion.isResume(); + } +} diff --git a/src/main/java/kim/half/service/BaseballGameResult.java b/src/main/java/kim/half/service/BaseballGameResult.java new file mode 100644 index 0000000000..671c3de522 --- /dev/null +++ b/src/main/java/kim/half/service/BaseballGameResult.java @@ -0,0 +1,32 @@ +package kim.half.service; + +import kim.half.domain.Baseballs; +import kim.half.ui.OutputBuilder; + + +public class BaseballGameResult { + + private static final int COUNT_ZERO = 0; + private final int strikeNumber; + private final int ballNumber; + + public BaseballGameResult(Baseballs computerBaseballs, Baseballs userBaseballs) { + strikeNumber = computerBaseballs.countStrike(userBaseballs); + ballNumber = computerBaseballs.countBall(userBaseballs); + } + + public String getResult() { + StringBuilder sb = new StringBuilder(); + + if (strikeNumber == COUNT_ZERO && ballNumber == COUNT_ZERO) { + sb.append(OutputBuilder.RESULT_NOTHING_MESSAGE); + } + if (strikeNumber != COUNT_ZERO) { + sb.append(strikeNumber).append(OutputBuilder.RESULT_STRIKE_MESSAGE); + } + if (ballNumber != COUNT_ZERO) { + sb.append(ballNumber).append(OutputBuilder.RESULT_BALL_MESSAGE); + } + return sb.toString(); + } +} diff --git a/src/main/java/kim/half/service/ResumeChecker.java b/src/main/java/kim/half/service/ResumeChecker.java new file mode 100644 index 0000000000..57f5eeb23b --- /dev/null +++ b/src/main/java/kim/half/service/ResumeChecker.java @@ -0,0 +1,36 @@ +package kim.half.service; + +import kim.half.ui.OutputBuilder; + +public class ResumeChecker { + + private static final int VALID_OPINION_INPUT_SIZE = 1; + private static final int RESUME_REPLY = 1; + private static final int STOP_REPLY = 2; + private static final char ZERO_CHAR = '0'; + + private final int opinion; + + public ResumeChecker(String userReply) { + isValidLength(userReply); + + this.opinion = userReply.charAt(0) - ZERO_CHAR; + isValidOpinionRange(opinion); + } + + private void isValidLength(String userReply) { + if (userReply.length() != VALID_OPINION_INPUT_SIZE) { + throw new IllegalArgumentException(OutputBuilder.RESUME_INPUT_SIZE_EXCEPTION_MESSAGE); + } + } + + private void isValidOpinionRange(int opinion) { + if ((opinion != RESUME_REPLY) && (opinion != STOP_REPLY)) { + throw new IllegalArgumentException(OutputBuilder.RESUME_INPUT_FORMAT_EXCEPTION_MESSAGE); + } + } + + public boolean isResume() { + return (opinion == RESUME_REPLY); + } +} diff --git a/src/main/java/kim/half/ui/InputScanner.java b/src/main/java/kim/half/ui/InputScanner.java new file mode 100644 index 0000000000..ef176dcf0e --- /dev/null +++ b/src/main/java/kim/half/ui/InputScanner.java @@ -0,0 +1,26 @@ +package kim.half.ui; + +import java.util.Scanner; + +public class InputScanner { + + private static Scanner scanner; + + public InputScanner(Scanner scanner) { + InputScanner.scanner = scanner; + } + + public static String scanInputNumbers() { + scanner = new Scanner(System.in); + + OutputBuilder.printRequestNumberInput(); + return scanner.nextLine(); + } + + public static String scanResumeChoice() { + scanner = new Scanner(System.in); + + OutputBuilder.printRequestResumeInput(); + return scanner.nextLine(); + } +} diff --git a/src/main/java/kim/half/ui/OutputBuilder.java b/src/main/java/kim/half/ui/OutputBuilder.java new file mode 100644 index 0000000000..8bedf112b9 --- /dev/null +++ b/src/main/java/kim/half/ui/OutputBuilder.java @@ -0,0 +1,35 @@ +package kim.half.ui; + +public class OutputBuilder { + + public static final String BASEBALLS_SIZE_EXCEPTION_MESSAGE = "3자리 수를 입력 해야 합니다."; + public static final String INPUT_NUMBER_FORMAT_EXCEPTION_MESSAGE = "1부터 9까지의 숫자만 입력 해야 합니다."; + public static final String RESUME_INPUT_SIZE_EXCEPTION_MESSAGE = "입력한 문자의 길이가 1을 넘을 수 없습니다."; + public static final String RESUME_INPUT_FORMAT_EXCEPTION_MESSAGE = "입력은 1 또는 2만 가능합니다."; + public static final String RESULT_NOTHING_MESSAGE = "낫싱"; + public static final String RESULT_STRIKE_MESSAGE = "스트라이크 "; + public static final String RESULT_BALL_MESSAGE = "볼 "; + public static final String REQUIRE_NUMBER_INPUT_MESSAGE = "세자리 숫자를 입력해주세요 : "; + public static final String REQUIRE_RESTART_INPUT_MESSAGE = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + public static final String CORRECT_ANSWER_AND_EXIT_MESSAGE = "3개의 숫자를 모두 맞히셨습니다! 게임 종료"; + + public static void printRequestNumberInput() { + System.out.print(REQUIRE_NUMBER_INPUT_MESSAGE); + } + + public static void printRequestResumeInput() { + System.out.println(REQUIRE_RESTART_INPUT_MESSAGE); + } + + public static void printBaseballGameResult(String result) { + System.out.println(result); + } + + public static void printCorrectAnswer() { + System.out.println(CORRECT_ANSWER_AND_EXIT_MESSAGE); + } + + public static void printExceptionMessage(String exceptionMessage) { + System.out.println(exceptionMessage); + } +} \ No newline at end of file diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/test/java/kim/half/domain/BaseballFactoryTest.java b/src/test/java/kim/half/domain/BaseballFactoryTest.java new file mode 100644 index 0000000000..d3b1563cd7 --- /dev/null +++ b/src/test/java/kim/half/domain/BaseballFactoryTest.java @@ -0,0 +1,42 @@ +package kim.half.domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class BaseballFactoryTest { + + @Test + public void testGenerateRandomBaseballs() { + Baseballs baseballs = BaseballFactory.generateRandomBaseballs(); + assertNotNull(baseballs); + assertEquals(3, baseballs.size()); + } + + @Test + public void testCreateBaseballs() { + String baseballNumbers = "123"; + Baseballs baseballs = BaseballFactory.createBaseballs(baseballNumbers); + assertNotNull(baseballs); + assertEquals(3, baseballs.size()); + assertEquals(1, baseballs.get(0).getNumber()); + assertEquals(2, baseballs.get(1).getNumber()); + assertEquals(3, baseballs.get(2).getNumber()); + } + + @Test + public void testCreateRandomBaseballs() { + Baseballs baseballs = BaseballFactory.createRandomBaseballs(); + assertNotNull(baseballs); + assertEquals(3, baseballs.size()); + assertTrue(baseballs.get(0).getNumber() >= 1 && baseballs.get(0).getNumber() <= 9); + assertTrue(baseballs.get(1).getNumber() >= 1 && baseballs.get(1).getNumber() <= 9); + assertTrue(baseballs.get(2).getNumber() >= 1 && baseballs.get(2).getNumber() <= 9); + assertNotEquals(baseballs.get(0).getNumber(), baseballs.get(1).getNumber()); + assertNotEquals(baseballs.get(0).getNumber(), baseballs.get(2).getNumber()); + assertNotEquals(baseballs.get(1).getNumber(), baseballs.get(2).getNumber()); + } +} \ No newline at end of file diff --git a/src/test/java/kim/half/domain/BaseballTest.java b/src/test/java/kim/half/domain/BaseballTest.java new file mode 100644 index 0000000000..170a1e4ca5 --- /dev/null +++ b/src/test/java/kim/half/domain/BaseballTest.java @@ -0,0 +1,35 @@ +package kim.half.domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class BaseballTest { + + @Test + public void testConstructorAndGetNumber() { + Baseball baseball = new Baseball(5); + assertEquals(5, baseball.getNumber()); + } + + @Test + public void testEqualsAndHashCode() { + Baseball baseball1 = new Baseball(3); + Baseball baseball2 = new Baseball(3); + Baseball baseball3 = new Baseball(7); + + assertEquals(baseball1, baseball2); + assertEquals(baseball1.hashCode(), baseball2.hashCode()); + + assertNotEquals(baseball1, baseball3); + assertNotEquals(baseball1.hashCode(), baseball3.hashCode()); + } + + @Test + public void testInvalidBaseballNumber() { + assertThrows(IllegalArgumentException.class, () -> new Baseball(0)); + assertThrows(IllegalArgumentException.class, () -> new Baseball(10)); + } +} \ No newline at end of file diff --git a/src/test/java/kim/half/domain/BaseballsTest.java b/src/test/java/kim/half/domain/BaseballsTest.java new file mode 100644 index 0000000000..6a8ba5a27e --- /dev/null +++ b/src/test/java/kim/half/domain/BaseballsTest.java @@ -0,0 +1,100 @@ +package kim.half.domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class BaseballsTest { + + @Test + public void testConstructorAndSizeAndGet() { + List baseballList = new ArrayList<>(); + baseballList.add(new Baseball(1)); + baseballList.add(new Baseball(2)); + baseballList.add(new Baseball(3)); + + Baseballs baseballs = new Baseballs(baseballList); + + assertEquals(3, baseballs.size()); + assertEquals(1, baseballs.get(0).getNumber()); + assertEquals(2, baseballs.get(1).getNumber()); + assertEquals(3, baseballs.get(2).getNumber()); + } + + @Test + public void testCountStrike() { + List baseballList1 = new ArrayList<>(); + baseballList1.add(new Baseball(1)); + baseballList1.add(new Baseball(2)); + baseballList1.add(new Baseball(3)); + + List baseballList2 = new ArrayList<>(); + baseballList2.add(new Baseball(1)); + baseballList2.add(new Baseball(5)); + baseballList2.add(new Baseball(3)); + + Baseballs baseballs1 = new Baseballs(baseballList1); + Baseballs baseballs2 = new Baseballs(baseballList2); + + assertEquals(2, baseballs1.countStrike(baseballs2)); + } + + @Test + public void testCountBall() { + List baseballList1 = new ArrayList<>(); + baseballList1.add(new Baseball(1)); + baseballList1.add(new Baseball(2)); + baseballList1.add(new Baseball(3)); + + List baseballList2 = new ArrayList<>(); + baseballList2.add(new Baseball(4)); + baseballList2.add(new Baseball(1)); + baseballList2.add(new Baseball(5)); + + Baseballs baseballs1 = new Baseballs(baseballList1); + Baseballs baseballs2 = new Baseballs(baseballList2); + + assertEquals(1, baseballs1.countBall(baseballs2)); + } + + @Test + public void testEqualsAndHashCode() { + List baseballList1 = new ArrayList<>(); + baseballList1.add(new Baseball(1)); + baseballList1.add(new Baseball(2)); + baseballList1.add(new Baseball(3)); + + List baseballList2 = new ArrayList<>(); + baseballList2.add(new Baseball(1)); + baseballList2.add(new Baseball(2)); + baseballList2.add(new Baseball(3)); + + List baseballList3 = new ArrayList<>(); + baseballList3.add(new Baseball(4)); + baseballList3.add(new Baseball(5)); + baseballList3.add(new Baseball(6)); + + Baseballs baseballs1 = new Baseballs(baseballList1); + Baseballs baseballs2 = new Baseballs(baseballList2); + Baseballs baseballs3 = new Baseballs(baseballList3); + + assertEquals(baseballs1, baseballs2); + assertEquals(baseballs1.hashCode(), baseballs2.hashCode()); + + assertNotEquals(baseballs1, baseballs3); + assertNotEquals(baseballs1.hashCode(), baseballs3.hashCode()); + } + + @Test + public void testInvalidSizeConstructor() { + List baseballList = new ArrayList<>(); + baseballList.add(new Baseball(1)); + baseballList.add(new Baseball(2)); + + assertThrows(IllegalArgumentException.class, () -> new Baseballs(baseballList)); + } +} \ No newline at end of file diff --git a/src/test/java/kim/half/service/BaseballGameResultTest.java b/src/test/java/kim/half/service/BaseballGameResultTest.java new file mode 100644 index 0000000000..9e707448e7 --- /dev/null +++ b/src/test/java/kim/half/service/BaseballGameResultTest.java @@ -0,0 +1,61 @@ +package kim.half.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import kim.half.domain.BaseballFactory; +import kim.half.domain.Baseballs; +import kim.half.ui.OutputBuilder; +import org.junit.jupiter.api.Test; + +public class BaseballGameResultTest { + + @Test + void testGetResultWithNothing() { + // Arrange + Baseballs computerBaseballs = BaseballFactory.createBaseballs("123"); + Baseballs userBaseballs = BaseballFactory.createBaseballs("456"); + BaseballGameResult baseballGameResult = new BaseballGameResult(computerBaseballs, + userBaseballs); + + // Test and Verify + assertEquals(OutputBuilder.RESULT_NOTHING_MESSAGE, baseballGameResult.getResult()); + } + + @Test + void testGetResultWithStrikeOnly() { + // Arrange + Baseballs computerBaseballs = BaseballFactory.createBaseballs("123"); + Baseballs userBaseballs = BaseballFactory.createBaseballs("156"); + BaseballGameResult baseballGameResult = new BaseballGameResult(computerBaseballs, + userBaseballs); + + // Test and Verify + assertEquals("1" + OutputBuilder.RESULT_STRIKE_MESSAGE, baseballGameResult.getResult()); + } + + @Test + void testGetResultWithBallOnly() { + // Arrange + Baseballs computerBaseballs = BaseballFactory.createBaseballs("123"); + Baseballs userBaseballs = BaseballFactory.createBaseballs("256"); + BaseballGameResult baseballGameResult = new BaseballGameResult(computerBaseballs, + userBaseballs); + + // Test and Verify + assertEquals("1" + OutputBuilder.RESULT_BALL_MESSAGE, baseballGameResult.getResult()); + } + + @Test + void testGetResultWithStrikeAndBall() { + // Arrange + Baseballs computerBaseballs = BaseballFactory.createBaseballs("123"); + Baseballs userBaseballs = BaseballFactory.createBaseballs("152"); + BaseballGameResult baseballGameResult = new BaseballGameResult(computerBaseballs, + userBaseballs); + + // Test and Verify + assertEquals( + "1" + OutputBuilder.RESULT_STRIKE_MESSAGE + "1" + OutputBuilder.RESULT_BALL_MESSAGE, + baseballGameResult.getResult()); + } +} diff --git a/src/test/java/kim/half/service/ResumeCheckerTest.java b/src/test/java/kim/half/service/ResumeCheckerTest.java new file mode 100644 index 0000000000..9cbc38e756 --- /dev/null +++ b/src/test/java/kim/half/service/ResumeCheckerTest.java @@ -0,0 +1,50 @@ +package kim.half.service; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class ResumeCheckerTest { + + @Test + void testConstructorWithValidInput() { + // Test + ResumeChecker resumeChecker = new ResumeChecker("1"); + + // Verify + assertNotNull(resumeChecker); + } + + @Test + void testConstructorWithInvalidInputSize() { + // Test and Verify + assertThrows(IllegalArgumentException.class, () -> new ResumeChecker("12")); + } + + @Test + void testConstructorWithInvalidOpinionRange() { + // Test and Verify + assertThrows(IllegalArgumentException.class, () -> new ResumeChecker("3")); + } + + @Test + void testIsResumeWithResumeReply() { + // Arrange + ResumeChecker resumeChecker = new ResumeChecker("1"); + + // Test and Verify + assertTrue(resumeChecker.isResume()); + } + + @Test + void testIsResumeWithStopReply() { + // Arrange + ResumeChecker resumeChecker = new ResumeChecker("2"); + + // Test and Verify + assertFalse(resumeChecker.isResume()); + } +} diff --git a/src/test/java/kim/half/ui/InputScannerTest.java b/src/test/java/kim/half/ui/InputScannerTest.java new file mode 100644 index 0000000000..99c9b191bb --- /dev/null +++ b/src/test/java/kim/half/ui/InputScannerTest.java @@ -0,0 +1,60 @@ +package kim.half.ui; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class InputScannerTest { + + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + private final ByteArrayInputStream inputStreamCaptor = new ByteArrayInputStream( + "123".getBytes()); + + @BeforeEach + void setUp() { + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @AfterEach + void tearDown() { + System.setIn(System.in); + } + + @Test + void testScanInputNumbers() { + // Redirect input + InputStream originalInput = System.in; + System.setIn(inputStreamCaptor); + + // Test + String input = InputScanner.scanInputNumbers(); + + // Restore original input + System.setIn(originalInput); + + // Verify + assertEquals("123", input); + } + + @Test + void testScanResumeChoice() { + // Redirect input + InputStream originalInput = System.in; + System.setIn(inputStreamCaptor); + + // Test + String choice = InputScanner.scanResumeChoice(); + + // Restore original input + System.setIn(originalInput); + + // Verify + assertEquals("123", choice); + } +} \ No newline at end of file diff --git a/src/test/java/kim/half/ui/OutputBuilderTest.java b/src/test/java/kim/half/ui/OutputBuilderTest.java new file mode 100644 index 0000000000..876ae2cb39 --- /dev/null +++ b/src/test/java/kim/half/ui/OutputBuilderTest.java @@ -0,0 +1,92 @@ +package kim.half.ui; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.Test; + +public class OutputBuilderTest { + + @Test + void testPrintRequestNumberInput() { + // Redirect System.out + ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + + // Test + OutputBuilder.printRequestNumberInput(); + + // Restore System.out + System.setOut(System.out); + + // Verify + assertEquals(OutputBuilder.REQUIRE_NUMBER_INPUT_MESSAGE, outputStreamCaptor.toString()); + } + + @Test + void testPrintRequestResumeInput() { + // Redirect System.out + ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + + // Test + OutputBuilder.printRequestResumeInput(); + + // Restore System.out + System.setOut(System.out); + + // Verify + assertEquals(OutputBuilder.REQUIRE_RESTART_INPUT_MESSAGE + "\n", + outputStreamCaptor.toString()); + } + + @Test + void testPrintBaseballGameResult() { + // Redirect System.out + ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + + // Test + OutputBuilder.printBaseballGameResult("Result"); + + // Restore System.out + System.setOut(System.out); + + // Verify + assertEquals("Result\n", outputStreamCaptor.toString()); + } + + @Test + void testPrintCorrectAnswer() { + // Redirect System.out + ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + + // Test + OutputBuilder.printCorrectAnswer(); + + // Restore System.out + System.setOut(System.out); + + // Verify + assertEquals(OutputBuilder.CORRECT_ANSWER_AND_EXIT_MESSAGE + "\n", + outputStreamCaptor.toString()); + } + + @Test + void testPrintExceptionMessage() { + // Redirect System.out + ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + + // Test + OutputBuilder.printExceptionMessage("Exception message"); + + // Restore System.out + System.setOut(System.out); + + // Verify + assertEquals("Exception message\n", outputStreamCaptor.toString()); + } +}