From 446a1167601d598d712a4be6d4b20c43fa2b26c9 Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Wed, 28 Jan 2026 18:06:03 +0900 Subject: [PATCH 01/10] docs(readme): update project description --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d7e8aee..f048a4b5 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# java-baseball-precourse \ No newline at end of file +## 구현할 기능 목록 + +### 1. 게임 실행 기능 +- [ ] 숫자 생성 기능 +- [ ] 스트라이크, 볼 판별 로직 +- [ ] 게임 실행 및 종료 로직 + +### 2. 입력 및 출력 관련 +- [ ] 숫자 입력 및 유효성 검증 +- [ ] 결과값 출력 format 구현 + +### 3. 테스트 코드 작성 +- [ ] Model 위주의 테스트 코드 \ No newline at end of file From d0023463d6f8042c7286f349265e1569bc23ce7a Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Wed, 28 Jan 2026 18:09:01 +0900 Subject: [PATCH 02/10] feat(NumberGenerator): implement random number generation Adds the ability to generate a unique 3-digit number for the game. The numbers are selected from 1 to 9 without repetition. --- src/main/java/baseball/NumberGenerator.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/baseball/NumberGenerator.java diff --git a/src/main/java/baseball/NumberGenerator.java b/src/main/java/baseball/NumberGenerator.java new file mode 100644 index 00000000..3f4d6698 --- /dev/null +++ b/src/main/java/baseball/NumberGenerator.java @@ -0,0 +1,20 @@ +package baseball; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class NumberGenerator { + public String generate() { + List numbers = new ArrayList<>(); + for (int i = 1; i <= 9; i++) { + numbers.add(i); + } + Collections.shuffle(numbers); + StringBuilder result = new StringBuilder(); + for (int i = 0; i < 3; i++) { + result.append(numbers.get(i)); + } + return result.toString(); + } +} \ No newline at end of file From 15f1d1d00e8e8c08f864e9cfefe4ff612955e611 Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Wed, 28 Jan 2026 18:12:38 +0900 Subject: [PATCH 03/10] feat(BaseballGame): implement strike and ball judgment logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces the core game logic to compare a player's input number against the target number, calculating strikes and balls. Updates README.md to mark "숫자 생성 기능" and "스트라이크, 볼 판별 로직" as completed. --- README.md | 4 +-- src/main/java/baseball/BaseballGame.java | 40 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/main/java/baseball/BaseballGame.java diff --git a/README.md b/README.md index f048a4b5..42a5f1fd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ ## 구현할 기능 목록 ### 1. 게임 실행 기능 -- [ ] 숫자 생성 기능 -- [ ] 스트라이크, 볼 판별 로직 +- [x] 숫자 생성 기능 +- [x] 스트라이크, 볼 판별 로직 - [ ] 게임 실행 및 종료 로직 ### 2. 입력 및 출력 관련 diff --git a/src/main/java/baseball/BaseballGame.java b/src/main/java/baseball/BaseballGame.java new file mode 100644 index 00000000..ddfc7ab5 --- /dev/null +++ b/src/main/java/baseball/BaseballGame.java @@ -0,0 +1,40 @@ +package baseball; + +public class BaseballGame { + private static final int GAME_SIZE = 3; + + private final String targetNumber; + + public BaseballGame(String targetNumber) { + this.targetNumber = targetNumber; + } + + public Result play(String inputNumber) { + int strikeCount = 0; + int ballCount = 0; + + for (int i = 0; i < GAME_SIZE; i++) { + char inputChar = inputNumber.charAt(i); + char targetChar = targetNumber.charAt(i); + + if (inputChar == targetChar) { + strikeCount++; + } else if (targetNumber.indexOf(inputChar) != -1) { + ballCount++; + } + } + return new Result(strikeCount, ballCount); + } + + private boolean isStrike(char inputChar, int index) { + return inputChar == targetNumber.charAt(index); + } + + private int countBall(char inputChar) { + if (!targetNumber.contains(String.valueOf(inputChar))) { + return 0; + } + return 1; + } +} + From 3d6412b5b4df7f3bb9fa86338b918db73881b5b5 Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Wed, 28 Jan 2026 18:16:19 +0900 Subject: [PATCH 04/10] feat(Result): add result data class Introduces the Result class to encapsulate the strike and ball counts for a game turn. This class also provides utility methods to check for a win condition (3 strikes) or a "nothing" result. --- README.md | 1 + src/main/java/baseball/Result.java | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/baseball/Result.java diff --git a/README.md b/README.md index 42a5f1fd..3cb4a40a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ### 1. 게임 실행 기능 - [x] 숫자 생성 기능 - [x] 스트라이크, 볼 판별 로직 +- [x] 스트라이크, 볼 관련 결과값 저장 - [ ] 게임 실행 및 종료 로직 ### 2. 입력 및 출력 관련 diff --git a/src/main/java/baseball/Result.java b/src/main/java/baseball/Result.java new file mode 100644 index 00000000..6938cb11 --- /dev/null +++ b/src/main/java/baseball/Result.java @@ -0,0 +1,28 @@ +package baseball; + +public class Result { + private final int strikeCount; + private final int ballCount; + + public Result(int strikeCount, int ballCount) { + this.strikeCount = strikeCount; + this.ballCount = ballCount; + } + + public int getStrikeCount() { + return strikeCount; + } + + public int getBallCount() { + return ballCount; + } + + public boolean isNothing() { + return strikeCount == 0 && ballCount == 0; + } + + public boolean isWin() { + return strikeCount == 3; + } +} + From 7604434e6e42d1cbea2021f1b721994d82c2bca4 Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Wed, 28 Jan 2026 18:26:39 +0900 Subject: [PATCH 05/10] feat(Application): implement main game execution logic Integrates number generation, game play, result formatting, and restart logic. Updates README with progress. Input validation pending. --- README.md | 5 +- src/main/java/baseball/Application.java | 90 +++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 src/main/java/baseball/Application.java diff --git a/README.md b/README.md index 3cb4a40a..c1c8e464 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,10 @@ - [x] 숫자 생성 기능 - [x] 스트라이크, 볼 판별 로직 - [x] 스트라이크, 볼 관련 결과값 저장 -- [ ] 게임 실행 및 종료 로직 ### 2. 입력 및 출력 관련 -- [ ] 숫자 입력 및 유효성 검증 -- [ ] 결과값 출력 format 구현 +- [x] 숫자 입력 및 결과값 출력 +- [ ] 숫자 입력 관련 유효성 검증 로직 도입 ### 3. 테스트 코드 작성 - [ ] Model 위주의 테스트 코드 \ No newline at end of file diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java new file mode 100644 index 00000000..1bea9f8a --- /dev/null +++ b/src/main/java/baseball/Application.java @@ -0,0 +1,90 @@ +package baseball; + +import java.util.Scanner; + +public class Application { + // 상수 처리: 대문자와 스네이크 케이스(_) 사용 + private static final int GAME_SIZE = 3; + private static final String ERROR_PREFIX = "[ERROR] "; + private static final String INPUT_PROMPT = "숫자를 입력해주세요 : "; + private static final String RESTART_PROMPT = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + + public static void main(String[] args) { + Application app = new Application(); + app.run(); + } + + // 실행 로직을 main에서 분리 + public void run() { + try (Scanner scanner = new Scanner(System.in)) { // try-with-resources로 자동 close + play(scanner); + } + } + + private void play(Scanner scanner) { + while (true) { + String targetNumber = new NumberGenerator().generate(); + playOneGame(scanner, targetNumber); + if (!shouldRestart(scanner)) { + return; + } + } + } + + private void playOneGame(Scanner scanner, String targetNumber) { + BaseballGame game = new BaseballGame(targetNumber); + while (true) { + // 숫자 입력 + String inputNumber = readUserNumber(scanner); + + // TODO :유효성 검증 로직 구현 + // if (!isValidUserNumber(inputNumber)) { + // continue; + // } + + Result result = game.play(inputNumber); + + // 결과값 출력 + System.out.println(formatResult(result)); + + if (result.isWin()) { + System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); + return; + } + } + } + + private String readUserNumber(Scanner scanner) { + System.out.print(INPUT_PROMPT); + return scanner.nextLine(); + } + + private boolean shouldRestart(Scanner scanner) { + System.out.println(RESTART_PROMPT); + String input = scanner.nextLine(); + if ("1".equals(input)) { + return true; + } + if ("2".equals(input)) { + return false; + } + System.out.println(ERROR_PREFIX + "1 또는 2를 입력해야 합니다."); + return shouldRestart(scanner); + } + + private String formatResult(Result result) { + if (result.isNothing()) { + return "낫싱"; + } + + StringBuilder res = new StringBuilder(); + if (result.getStrikeCount() > 0) { + res.append(result.getStrikeCount()).append(" 스트라이크 "); + } + if (result.getBallCount() > 0) { + res.append(result.getBallCount()).append(" 볼"); + } + + return res.toString().trim(); + } +} \ No newline at end of file From 70e168ca9840d733681f0808f65e875037ed1ec6 Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Wed, 28 Jan 2026 18:31:37 +0900 Subject: [PATCH 06/10] refactor(BaseballGame): refactor play method to adhere to coding conventions Refactors the 'play' method in BaseballGame to eliminate the use of the 'else' keyword and improve method single responsibility. Extracts strike and ball counting into dedicated private methods for clarity and better adherence to stated coding guidelines. --- src/main/java/baseball/BaseballGame.java | 37 ++++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/java/baseball/BaseballGame.java b/src/main/java/baseball/BaseballGame.java index ddfc7ab5..549b7502 100644 --- a/src/main/java/baseball/BaseballGame.java +++ b/src/main/java/baseball/BaseballGame.java @@ -10,31 +10,36 @@ public BaseballGame(String targetNumber) { } public Result play(String inputNumber) { - int strikeCount = 0; - int ballCount = 0; + int strikeCount = countStrikes(inputNumber); + int ballCount = countBalls(inputNumber); + return new Result(strikeCount, ballCount); + } + private int countStrikes(String inputNumber) { + int strikeCount = 0; for (int i = 0; i < GAME_SIZE; i++) { - char inputChar = inputNumber.charAt(i); - char targetChar = targetNumber.charAt(i); - - if (inputChar == targetChar) { + if (isStrike(inputNumber.charAt(i), i)) { strikeCount++; - } else if (targetNumber.indexOf(inputChar) != -1) { + } + } + return strikeCount; + } + + private int countBalls(String inputNumber) { + int ballCount = 0; + for (int i = 0; i < GAME_SIZE; i++) { + if (isBall(inputNumber.charAt(i), i)) { ballCount++; } } - return new Result(strikeCount, ballCount); + return ballCount; } private boolean isStrike(char inputChar, int index) { - return inputChar == targetNumber.charAt(index); + return targetNumber.charAt(index) == inputChar; } - private int countBall(char inputChar) { - if (!targetNumber.contains(String.valueOf(inputChar))) { - return 0; - } - return 1; + private boolean isBall(char inputChar, int index) { + return !isStrike(inputChar, index) && targetNumber.contains(String.valueOf(inputChar)); } -} - +} \ No newline at end of file From d66e2aee31e6ef63ce33b1f22b20306643ab0499 Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Wed, 28 Jan 2026 18:34:20 +0900 Subject: [PATCH 07/10] refactor(BaseballGame): reduce indent depth in counter methods Refactors 'countStrikes' and 'countBalls' methods to adhere to the maximum indent depth rule (max 2). Extracts increment logic into new helper methods ('getStrikeIncrement', 'getBallIncrement') to reduce nesting. --- src/main/java/baseball/BaseballGame.java | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/baseball/BaseballGame.java b/src/main/java/baseball/BaseballGame.java index 549b7502..22809894 100644 --- a/src/main/java/baseball/BaseballGame.java +++ b/src/main/java/baseball/BaseballGame.java @@ -18,23 +18,33 @@ public Result play(String inputNumber) { private int countStrikes(String inputNumber) { int strikeCount = 0; for (int i = 0; i < GAME_SIZE; i++) { - if (isStrike(inputNumber.charAt(i), i)) { - strikeCount++; - } + strikeCount += getStrikeIncrement(inputNumber.charAt(i), i); } return strikeCount; } + private int getStrikeIncrement(char inputChar, int index) { + if (isStrike(inputChar, index)) { + return 1; + } + return 0; + } + private int countBalls(String inputNumber) { int ballCount = 0; for (int i = 0; i < GAME_SIZE; i++) { - if (isBall(inputNumber.charAt(i), i)) { - ballCount++; - } + ballCount += getBallIncrement(inputNumber.charAt(i), i); } return ballCount; } + private int getBallIncrement(char inputChar, int index) { + if (isBall(inputChar, index)) { + return 1; + } + return 0; + } + private boolean isStrike(char inputChar, int index) { return targetNumber.charAt(index) == inputChar; } @@ -42,4 +52,4 @@ private boolean isStrike(char inputChar, int index) { private boolean isBall(char inputChar, int index) { return !isStrike(inputChar, index) && targetNumber.contains(String.valueOf(inputChar)); } -} \ No newline at end of file +} From 48e5755d26b07ade068209191b6e31b003507f7b Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Wed, 28 Jan 2026 18:38:51 +0900 Subject: [PATCH 08/10] refactor(Application): refactor to comply with coding conventions Refactors 'Application.java' to strictly adhere to coding conventions. This includes reducing indent depth to a maximum of 2 by extracting helper methods, ensuring all methods are under 15 lines, and removing recursion in favor of loops. The validation logic and game loops were the main areas of improvement. --- README.md | 2 +- src/main/java/baseball/Application.java | 111 +++++++++++++++++------- 2 files changed, 79 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index c1c8e464..5bebf55c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ### 2. 입력 및 출력 관련 - [x] 숫자 입력 및 결과값 출력 -- [ ] 숫자 입력 관련 유효성 검증 로직 도입 +- [x] 숫자 입력 관련 유효성 검증 로직 도입 ### 3. 테스트 코드 작성 - [ ] Model 위주의 테스트 코드 \ No newline at end of file diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java index 1bea9f8a..c3f3a97e 100644 --- a/src/main/java/baseball/Application.java +++ b/src/main/java/baseball/Application.java @@ -3,55 +3,52 @@ import java.util.Scanner; public class Application { - // 상수 처리: 대문자와 스네이크 케이스(_) 사용 private static final int GAME_SIZE = 3; private static final String ERROR_PREFIX = "[ERROR] "; private static final String INPUT_PROMPT = "숫자를 입력해주세요 : "; private static final String RESTART_PROMPT = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + private static final String GAME_OVER_MESSAGE = "3개의 숫자를 모두 맞히셨습니다! 게임 종료"; public static void main(String[] args) { Application app = new Application(); app.run(); } - // 실행 로직을 main에서 분리 public void run() { - try (Scanner scanner = new Scanner(System.in)) { // try-with-resources로 자동 close + try (Scanner scanner = new Scanner(System.in)) { play(scanner); } } private void play(Scanner scanner) { - while (true) { + boolean restart = true; + while (restart) { String targetNumber = new NumberGenerator().generate(); playOneGame(scanner, targetNumber); - if (!shouldRestart(scanner)) { - return; - } + restart = shouldRestart(scanner); } } private void playOneGame(Scanner scanner, String targetNumber) { BaseballGame game = new BaseballGame(targetNumber); - while (true) { - // 숫자 입력 + boolean isFinished = false; + while (!isFinished) { String inputNumber = readUserNumber(scanner); - - // TODO :유효성 검증 로직 구현 - // if (!isValidUserNumber(inputNumber)) { - // continue; - // } - + if (!isValidUserNumber(inputNumber)) { + continue; + } Result result = game.play(inputNumber); + isFinished = processGameResult(result); + } + } - // 결과값 출력 - System.out.println(formatResult(result)); - - if (result.isWin()) { - System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); - return; - } + private boolean processGameResult(Result result) { + System.out.println(formatResult(result)); + if (result.isWin()) { + System.out.println(GAME_OVER_MESSAGE); + return true; } + return false; } private String readUserNumber(Scanner scanner) { @@ -59,24 +56,73 @@ private String readUserNumber(Scanner scanner) { return scanner.nextLine(); } - private boolean shouldRestart(Scanner scanner) { - System.out.println(RESTART_PROMPT); - String input = scanner.nextLine(); - if ("1".equals(input)) { - return true; + private boolean isValidUserNumber(String inputNumber) { + if (inputNumber == null) { + System.out.println(ERROR_PREFIX + "입력값이 올바르지 않습니다."); + return false; + } + if (inputNumber.length() != GAME_SIZE) { + System.out.println(ERROR_PREFIX + "3자리 숫자를 입력해야 합니다."); + return false; } - if ("2".equals(input)) { + if (!areDigitsValid(inputNumber)) { return false; } - System.out.println(ERROR_PREFIX + "1 또는 2를 입력해야 합니다."); - return shouldRestart(scanner); + if (hasDuplicateDigits(inputNumber)) { + System.out.println(ERROR_PREFIX + "서로 다른 숫자를 입력해야 합니다."); + return false; + } + return true; + } + + private boolean areDigitsValid(String inputNumber) { + for (int i = 0; i < GAME_SIZE; i++) { + char c = inputNumber.charAt(i); + if (c < '1' || c > '9') { + System.out.println(ERROR_PREFIX + "1부터 9까지 숫자만 입력해야 합니다."); + return false; + } + } + return true; + } + + private boolean hasDuplicateDigits(String inputNumber) { + for (int i = 0; i < GAME_SIZE; i++) { + if (isCharDuplicatedInRestOfString(inputNumber, i)) { + return true; + } + } + return false; + } + + private boolean isCharDuplicatedInRestOfString(String inputNumber, int index) { + char currentChar = inputNumber.charAt(index); + for (int j = index + 1; j < GAME_SIZE; j++) { + if (currentChar == inputNumber.charAt(j)) { + return true; + } + } + return false; + } + + private boolean shouldRestart(Scanner scanner) { + while (true) { + System.out.println(RESTART_PROMPT); + String input = scanner.nextLine(); + if ("1".equals(input)) { + return true; + } + if ("2".equals(input)) { + return false; + } + System.out.println(ERROR_PREFIX + "1 또는 2를 입력해야 합니다."); + } } private String formatResult(Result result) { if (result.isNothing()) { return "낫싱"; } - StringBuilder res = new StringBuilder(); if (result.getStrikeCount() > 0) { res.append(result.getStrikeCount()).append(" 스트라이크 "); @@ -84,7 +130,6 @@ private String formatResult(Result result) { if (result.getBallCount() > 0) { res.append(result.getBallCount()).append(" 볼"); } - return res.toString().trim(); } -} \ No newline at end of file +} From 234317e77ae9ef924446e070cab296f9fc5ea22f Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Sat, 7 Feb 2026 15:05:48 +0900 Subject: [PATCH 09/10] docs(README): add unit test plan Adds a detailed unit test plan to README.md, outlining the test cases for NumberGenerator, BaseballGame, and Result classes. --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5bebf55c..d6e84222 100644 --- a/README.md +++ b/README.md @@ -9,5 +9,17 @@ - [x] 숫자 입력 및 결과값 출력 - [x] 숫자 입력 관련 유효성 검증 로직 도입 -### 3. 테스트 코드 작성 -- [ ] Model 위주의 테스트 코드 \ No newline at end of file +### 3. 단위 테스트 계획 +- [ ] `NumberGeneratorTest` + - [ ] 3자리의 숫자인지 검증 + - [ ] 각 자리수가 중복되지 않는지 검증 + - [ ] 각 자리수가 1-9 사이의 숫자인지 검증 +- [ ] `BaseballGameTest` (target: "123") + - [ ] 3 스트라이크 (e.g., "123") + - [ ] 3 볼 (e.g., "312") + - [ ] 1 스트라이크 2 볼 (e.g., "132") + - [ ] 낫싱 (e.g., "456") +- [ ] `ResultTest` + - [ ] 3 스트라이크일 때 `isWin()`이 true인지 검증 + - [ ] 3 스트라이크가 아닐 때 `isWin()`이 false인지 검증 + - [ ] 0 스트라이크, 0 볼일 때 `isNothing()`이 true인지 검증 From ef893f5ec5d4cf257124faa84d649e0a209bc686 Mon Sep 17 00:00:00 2001 From: "charlie.woo" Date: Sat, 7 Feb 2026 15:15:38 +0900 Subject: [PATCH 10/10] test(domain): implement unit tests for domain logic Adds comprehensive unit tests for the core domain logic: NumberGenerator, BaseballGame, and Result. Also updates the README to reflect the completion of the test plan. --- README.md | 26 ++++----- src/test/java/baseball/BaseballGameTest.java | 40 +++++++++++++ .../java/baseball/NumberGeneratorTest.java | 41 +++++++++++++ src/test/java/baseball/ResultTest.java | 57 +++++++++++++++++++ 4 files changed, 151 insertions(+), 13 deletions(-) create mode 100644 src/test/java/baseball/BaseballGameTest.java create mode 100644 src/test/java/baseball/NumberGeneratorTest.java create mode 100644 src/test/java/baseball/ResultTest.java diff --git a/README.md b/README.md index d6e84222..787b5968 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,16 @@ - [x] 숫자 입력 관련 유효성 검증 로직 도입 ### 3. 단위 테스트 계획 -- [ ] `NumberGeneratorTest` - - [ ] 3자리의 숫자인지 검증 - - [ ] 각 자리수가 중복되지 않는지 검증 - - [ ] 각 자리수가 1-9 사이의 숫자인지 검증 -- [ ] `BaseballGameTest` (target: "123") - - [ ] 3 스트라이크 (e.g., "123") - - [ ] 3 볼 (e.g., "312") - - [ ] 1 스트라이크 2 볼 (e.g., "132") - - [ ] 낫싱 (e.g., "456") -- [ ] `ResultTest` - - [ ] 3 스트라이크일 때 `isWin()`이 true인지 검증 - - [ ] 3 스트라이크가 아닐 때 `isWin()`이 false인지 검증 - - [ ] 0 스트라이크, 0 볼일 때 `isNothing()`이 true인지 검증 +- [x] `NumberGeneratorTest` + - [x] 3자리의 숫자인지 검증 + - [x] 각 자리수가 중복되지 않는지 검증 + - [x] 각 자리수가 1-9 사이의 숫자인지 검증 +- [x] `BaseballGameTest` (target: "123") + - [x] 3 스트라이크 (e.g., "123") + - [x] 3 볼 (e.g., "312") + - [x] 1 스트라이크 2 볼 (e.g., "132") + - [x] 낫싱 (e.g., "456") +- [x] `ResultTest` + - [x] 3 스트라이크일 때 `isWin()`이 true인지 검증 + - [x] 3 스트라이크가 아닐 때 `isWin()`이 false인지 검증 + - [x] 0 스트라이크, 0 볼일 때 `isNothing()`이 true인지 검증 \ No newline at end of file diff --git a/src/test/java/baseball/BaseballGameTest.java b/src/test/java/baseball/BaseballGameTest.java new file mode 100644 index 00000000..0ffc687e --- /dev/null +++ b/src/test/java/baseball/BaseballGameTest.java @@ -0,0 +1,40 @@ +package baseball; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class BaseballGameTest { + + private final BaseballGame game = new BaseballGame("123"); + + @ParameterizedTest + @MethodSource("gameScenarios") + @DisplayName("스트라이크와 볼 개수를 정확히 계산한다") + void play_calculatesStrikesAndBallsCorrectly(String input, int expectedStrikes, int expectedBalls) { + Result result = game.play(input); + + assertThat(result.getStrikeCount()).isEqualTo(expectedStrikes); + assertThat(result.getBallCount()).isEqualTo(expectedBalls); + } + + static Stream gameScenarios() { + return Stream.of( + // 계획된 테스트 케이스 + Arguments.of("123", 3, 0), // 3 스트라이크 + Arguments.of("312", 0, 3), // 3 볼 + Arguments.of("132", 1, 2), // 1 스트라이크 2 볼 + Arguments.of("456", 0, 0), // 낫싱 + + // 견고성을 위한 추가 케이스 + Arguments.of("124", 2, 0), // 2 스트라이크 + Arguments.of("415", 0, 1), // 1 볼 + Arguments.of("142", 1, 1) // 1 스트라이크 1 볼 + ); + } +} \ No newline at end of file diff --git a/src/test/java/baseball/NumberGeneratorTest.java b/src/test/java/baseball/NumberGeneratorTest.java new file mode 100644 index 00000000..57db9855 --- /dev/null +++ b/src/test/java/baseball/NumberGeneratorTest.java @@ -0,0 +1,41 @@ +package baseball; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.RepeatedTest; + +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class NumberGeneratorTest { + + private final NumberGenerator generator = new NumberGenerator(); + + @RepeatedTest(10) + @DisplayName("생성된 숫자는 3자리이다") + void generates_a_3_digit_number() { + String number = generator.generate(); + assertThat(number).hasSize(3); + } + + @RepeatedTest(10) + @DisplayName("생성된 숫자는 1-9 사이의 값이다") + void generates_digits_between_1_and_9() { + String number = generator.generate(); + for (char c : number.toCharArray()) { + assertThat(c).isGreaterThanOrEqualTo('1').isLessThanOrEqualTo('9'); + } + } + + @RepeatedTest(10) + @DisplayName("생성된 숫자는 서로 다른 수로 이루어져 있다") + void generates_a_number_with_unique_digits() { + String number = generator.generate(); + Set uniqueDigits = new HashSet<>(); + for (char c : number.toCharArray()) { + uniqueDigits.add(c); + } + assertThat(uniqueDigits).hasSize(3); + } +} \ No newline at end of file diff --git a/src/test/java/baseball/ResultTest.java b/src/test/java/baseball/ResultTest.java new file mode 100644 index 00000000..9a8fd5d6 --- /dev/null +++ b/src/test/java/baseball/ResultTest.java @@ -0,0 +1,57 @@ +package baseball; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Result 클래스") +class ResultTest { + + @Nested + @DisplayName("isWin 메소드는") + class IsWinTest { + + @Test + @DisplayName("3 스트라이크일 때 true를 반환한다") + void returns_true_for_3_strikes() { + Result result = new Result(3, 0); + assertThat(result.isWin()).isTrue(); + } + + @Test + @DisplayName("3 스트라이크가 아닐 때 false를 반환한다") + void returns_false_for_less_than_3_strikes() { + Result resultWith2Strikes = new Result(2, 1); + Result resultWith0Strikes = new Result(0, 2); + + assertThat(resultWith2Strikes.isWin()).isFalse(); + assertThat(resultWith0Strikes.isWin()).isFalse(); + } + } + + @Nested + @DisplayName("isNothing 메소드는") + class IsNothingTest { + + @Test + @DisplayName("0 스트라이크, 0 볼일 때 true를 반환한다") + void returns_true_for_0_strikes_and_0_balls() { + Result result = new Result(0, 0); + assertThat(result.isNothing()).isTrue(); + } + + @Test + @DisplayName("스트라이크나 볼이 하나라도 있으면 false를 반환한다") + void returns_false_if_strikes_or_balls_exist() { + Result resultWithStrikes = new Result(1, 0); + Result resultWithBalls = new Result(0, 2); + Result resultWithBoth = new Result(1, 1); + + assertThat(resultWithStrikes.isNothing()).isFalse(); + assertThat(resultWithBalls.isNothing()).isFalse(); + assertThat(resultWithBoth.isNothing()).isFalse(); + } + } +}