From 6dc8888398866062f462be765586ab427d11de79 Mon Sep 17 00:00:00 2001 From: moongua404 Date: Sat, 8 Feb 2025 15:19:49 +0900 Subject: [PATCH 1/8] =?UTF-8?q?docs(README):=20=EB=A6=AC=EB=93=9C=EB=AF=B8?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 과제 내용, 코드 흐름, 구현 기능 목록 작성 - 코드 흐름에서 flow diagram 작성 --- README.md | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d0286c8..1b77aa0 100644 --- a/README.md +++ b/README.md @@ -1 +1,103 @@ -# java-racingcar-precourse +# java-racingCar-precourse + + +> 콩 번째로 하는 우테코의 콩 번째 미션 \ +> 콩 번째로 하는 우테코의 콩 번째 미션 + +
+ 과제 세부 내용 + +## 과제 내용 +초간단 자동차 경주 게임을 구현한다. + +- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. +- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. +- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. +- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. +- 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. +- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. +- 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다. +- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시킨 후 애플리케이션은 종료되어야 한다. + +### 입출력 +- 입력 + - 경주 할 자동차 이름 + - 시도할 횟수 +- 출력 + - 각 차수별 실행 결과 + - 우승자 안내 문구 + +ex) + +``` +경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분) +pobi,woni,jun +시도할 회수는 몇회인가요? +5 + +실행 결과 +pobi : - +woni : +jun : - + +pobi : -- +woni : - +jun : -- + +pobi : --- +woni : -- +jun : --- + +pobi : ---- +woni : --- +jun : ---- + +pobi : ----- +woni : ---- +jun : ----- + +최종 우승자 : pobi, jun +``` + +
+ +## 코드 흐름 +- 사용자의 이름 및 시도 횟수를 입력받는다. 이름의 유효성을 검증한다. +- 랜덤으로 자동차의 전진 횟수를 더한다. +- 최종 우승자를 출력한다. + +```mermaid +sequenceDiagram + participant View + participant Controller + participant Model + + Controller->>View: 입력 대기 + View->>Controller: 이름, 시도횟수 반환 + Controller->>Model: 이름, 시도횟수 전달 + Model->>Controller: 게임 생성 + loop 시도횟수만큼 반복 + Controller->>Model: 실행 대기 + Model->>Controller: 실행 결과 반환 + Controller->>View: 실행 결과 전달 + View->>View: 결과 출력 + View->>Controller: - + end + Controller->>Model: 우승자 요청 + Model->>Controller: 우승자 반환 + Controller->>View: 우승자 전달 + View->>View: 우승자 출력 + View->>Controller: - + Controller->>Controller: 프로그램 종료 + +``` + +## 구현 기능 목록 +- 입출력 + - [ ] 사용자 이름 입력 + - [ ] 시도 횟수 입력 + - [ ] 차수별 실행 결과 출력 + - [ ] 최종 우승자 출력 +- 자동차 전진 + - [ ] 여러 자동차의 상태 관리 + - [ ] 랜덤 추출 기능 \ No newline at end of file From 788844c579b96771381a477178ea56924460d6ab Mon Sep 17 00:00:00 2001 From: moongua404 Date: Mon, 10 Feb 2025 15:30:39 +0900 Subject: [PATCH 2/8] =?UTF-8?q?chore(init):=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EA=B5=AC?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MVC 패턴에 맞춰 패키지 구성 - AppConfig 클래스 생성 --- src/main/java/racingcar/AppConfig.java | 4 ++++ src/main/java/racingcar/controller/RacingcarController.java | 4 ++++ src/main/java/racingcar/view/InputView.java | 4 ++++ src/main/java/racingcar/view/OutputView.java | 4 ++++ 4 files changed, 16 insertions(+) create mode 100644 src/main/java/racingcar/AppConfig.java create mode 100644 src/main/java/racingcar/controller/RacingcarController.java create mode 100644 src/main/java/racingcar/view/InputView.java create mode 100644 src/main/java/racingcar/view/OutputView.java diff --git a/src/main/java/racingcar/AppConfig.java b/src/main/java/racingcar/AppConfig.java new file mode 100644 index 0000000..d71d934 --- /dev/null +++ b/src/main/java/racingcar/AppConfig.java @@ -0,0 +1,4 @@ +package racingcar; + +public class AppConfig { +} diff --git a/src/main/java/racingcar/controller/RacingcarController.java b/src/main/java/racingcar/controller/RacingcarController.java new file mode 100644 index 0000000..2f6b533 --- /dev/null +++ b/src/main/java/racingcar/controller/RacingcarController.java @@ -0,0 +1,4 @@ +package racingcar.controller; + +public class RacingcarController { +} diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java new file mode 100644 index 0000000..2361ba2 --- /dev/null +++ b/src/main/java/racingcar/view/InputView.java @@ -0,0 +1,4 @@ +package racingcar.view; + +public class InputView { +} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java new file mode 100644 index 0000000..abf938a --- /dev/null +++ b/src/main/java/racingcar/view/OutputView.java @@ -0,0 +1,4 @@ +package racingcar.view; + +public class OutputView { +} From d6d3a0ae74d5585b57809e09a7e1c6355982ad10 Mon Sep 17 00:00:00 2001 From: moongua404 Date: Wed, 12 Feb 2025 00:51:04 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat(view):=20=EC=9E=85=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=20=EB=B7=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - InputProvider 인터페이스로 의존성 분리 - 테스트 코드 작성 --- .../racingcar/model/dto/CarMovementDto.java | 27 +++++++++++++++++ .../racingcar/utils/MessageConstants.java | 19 ++++++++++++ src/main/java/racingcar/view/InputView.java | 26 +++++++++++++++++ src/main/java/racingcar/view/OutputView.java | 25 ++++++++++++++++ .../view/provider/InputProvider.java | 7 +++++ .../view/provider/WoowaInputProvider.java | 10 +++++++ .../java/racingcar/MockInputProvider.java | 21 ++++++++++++++ src/test/java/racingcar/ViewTest.java | 29 +++++++++++++++++++ 8 files changed, 164 insertions(+) create mode 100644 src/main/java/racingcar/model/dto/CarMovementDto.java create mode 100644 src/main/java/racingcar/utils/MessageConstants.java create mode 100644 src/main/java/racingcar/view/provider/InputProvider.java create mode 100644 src/main/java/racingcar/view/provider/WoowaInputProvider.java create mode 100644 src/test/java/racingcar/MockInputProvider.java create mode 100644 src/test/java/racingcar/ViewTest.java diff --git a/src/main/java/racingcar/model/dto/CarMovementDto.java b/src/main/java/racingcar/model/dto/CarMovementDto.java new file mode 100644 index 0000000..dfa2dfa --- /dev/null +++ b/src/main/java/racingcar/model/dto/CarMovementDto.java @@ -0,0 +1,27 @@ +package racingcar.model.dto; + +public class CarMovementDto { + private String name; + private int movement; + + public CarMovementDto(String name, int movement) { + this.name = name; + this.movement = movement; + } + + public String getName() { + return name; + } + + public int getMovement() { + return movement; + } + + public void setName(String name) { + this.name = name; + } + + public void setMovement(int movement) { + this.movement = movement; + } +} diff --git a/src/main/java/racingcar/utils/MessageConstants.java b/src/main/java/racingcar/utils/MessageConstants.java new file mode 100644 index 0000000..a81989e --- /dev/null +++ b/src/main/java/racingcar/utils/MessageConstants.java @@ -0,0 +1,19 @@ +package racingcar.utils; + +public enum MessageConstants { + INPUT_CAR_NAME_GUIDE("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"), + INPUT_TRIAL_NUMBER_GUIDE("시도할 회수는 몇회인가요?"), + OUTPUT_EXECUTE_RESULT_GUIDE("실행 결과"), + OUTPUT_FINAL_WINNER_GUIDE("최종 우승자 : "), + OUTPUT_CAR_MOVEMENT("%s : "); + + private final String message; + + MessageConstants(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index 2361ba2..6aecbb5 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -1,4 +1,30 @@ package racingcar.view; +import racingcar.utils.MessageConstants; +import racingcar.view.provider.InputProvider; + public class InputView { + private final InputProvider inputProvider; + + public InputView(InputProvider inputProvider) { + this.inputProvider = inputProvider; + } + + public String getCarName() { + try { + System.out.println(MessageConstants.INPUT_CAR_NAME_GUIDE.getMessage()); + return inputProvider.readLine(); + } catch (Exception exception) { + throw new IllegalArgumentException("[Error] 입출력 과정에서 예외가 발생했습니다. "); + } + } + + public int getTrialNumber() { + try { + System.out.println(MessageConstants.INPUT_TRIAL_NUMBER_GUIDE.getMessage()); + return Integer.parseInt(inputProvider.readLine()); + } catch (Exception exception) { + throw new IllegalArgumentException("[Error] 입출력 과정에서 예외가 발생했습니다. "); + } + } } diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index abf938a..c8ba0dd 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -1,4 +1,29 @@ package racingcar.view; +import java.util.List; +import racingcar.model.dto.CarMovementDto; +import racingcar.utils.MessageConstants; + public class OutputView { + private final String SEPARATOR = ", "; + private final String MOVEMENT_DELIMITER = "-"; + + public void printResultGuide() { + System.out.println(MessageConstants.OUTPUT_EXECUTE_RESULT_GUIDE.getMessage()); + } + + public void printPlayerResult(List playerResult) { + playerResult.forEach((result) -> + System.out.printf( + MessageConstants.OUTPUT_CAR_MOVEMENT.getMessage(), result.getName() + + MOVEMENT_DELIMITER.repeat(result.getMovement())) + ); + } + + public void printWinner(List names) { + System.out.println( + MessageConstants.OUTPUT_FINAL_WINNER_GUIDE.getMessage() + + String.join(SEPARATOR, names) + ); + } } diff --git a/src/main/java/racingcar/view/provider/InputProvider.java b/src/main/java/racingcar/view/provider/InputProvider.java new file mode 100644 index 0000000..539c856 --- /dev/null +++ b/src/main/java/racingcar/view/provider/InputProvider.java @@ -0,0 +1,7 @@ +package racingcar.view.provider; + +import java.io.IOException; + +public interface InputProvider { + String readLine() throws IOException; +} diff --git a/src/main/java/racingcar/view/provider/WoowaInputProvider.java b/src/main/java/racingcar/view/provider/WoowaInputProvider.java new file mode 100644 index 0000000..d1e1270 --- /dev/null +++ b/src/main/java/racingcar/view/provider/WoowaInputProvider.java @@ -0,0 +1,10 @@ +package racingcar.view.provider; + +import camp.nextstep.edu.missionutils.Console; + +public class WoowaInputProvider implements InputProvider { + @Override + public String readLine() { + return Console.readLine(); + } +} diff --git a/src/test/java/racingcar/MockInputProvider.java b/src/test/java/racingcar/MockInputProvider.java new file mode 100644 index 0000000..9605d02 --- /dev/null +++ b/src/test/java/racingcar/MockInputProvider.java @@ -0,0 +1,21 @@ +package racingcar; + +import java.io.IOException; +import racingcar.view.provider.InputProvider; + +public class MockInputProvider implements InputProvider { + private final String[] inputs; + private int index = 0; + + public MockInputProvider(String... inputs) { + this.inputs = inputs; + } + + @Override + public String readLine() throws IOException { + if (index < inputs.length) { + return inputs[index++]; + } + throw new IOException(); + } +} diff --git a/src/test/java/racingcar/ViewTest.java b/src/test/java/racingcar/ViewTest.java new file mode 100644 index 0000000..7806808 --- /dev/null +++ b/src/test/java/racingcar/ViewTest.java @@ -0,0 +1,29 @@ +package racingcar; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import racingcar.view.InputView; + +public class ViewTest { + private InputView inputView; + + @BeforeEach + void setUp() { + inputView = new InputView(new MockInputProvider("pobi,woni,jun", "5")); + } + + @Test + void carNameInputTest() { + String carNames = inputView.getCarName(); + assertEquals("pobi,woni,jun", carNames); + } + + @Test + void trialInputTest() { + inputView.getCarName(); + int trialNumber = inputView.getTrialNumber(); + assertEquals(5, trialNumber); + } +} From a5dbfed996c134f867dca5d39001e9725e0b5f9b Mon Sep 17 00:00:00 2001 From: moongua404 Date: Wed, 12 Feb 2025 15:06:22 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat(model):=20Car,=20Race=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Race 모델을 통해 게임 전반 관리 - 이동 여부(랜덤 로직) Suplier로 의존성 분리 - 테스트코드 작성 (예제 모킹) --- src/main/java/racingcar/model/dto/Car.java | 19 +++++ .../racingcar/model/dto/CarMovementDto.java | 12 +++ src/main/java/racingcar/model/dto/Race.java | 50 ++++++++++++ src/main/java/racingcar/view/OutputView.java | 4 +- .../java/racingcar/MockRandomProvider.java | 17 ++++ src/test/java/racingcar/ModelTest.java | 65 +++++++++++++++ src/test/java/racingcar/ViewTest.java | 81 ++++++++++++++++--- 7 files changed, 236 insertions(+), 12 deletions(-) create mode 100644 src/main/java/racingcar/model/dto/Car.java create mode 100644 src/main/java/racingcar/model/dto/Race.java create mode 100644 src/test/java/racingcar/MockRandomProvider.java create mode 100644 src/test/java/racingcar/ModelTest.java diff --git a/src/main/java/racingcar/model/dto/Car.java b/src/main/java/racingcar/model/dto/Car.java new file mode 100644 index 0000000..9637ecf --- /dev/null +++ b/src/main/java/racingcar/model/dto/Car.java @@ -0,0 +1,19 @@ +package racingcar.model.dto; + +public class Car { + private final String name; + private int position; + + public Car(String name) { + this.name = name; + position = 0; + } + + public CarMovementDto getData() { + return new CarMovementDto(name, position); + } + + public void move() { + position++; + } +} diff --git a/src/main/java/racingcar/model/dto/CarMovementDto.java b/src/main/java/racingcar/model/dto/CarMovementDto.java index dfa2dfa..a99e4c9 100644 --- a/src/main/java/racingcar/model/dto/CarMovementDto.java +++ b/src/main/java/racingcar/model/dto/CarMovementDto.java @@ -24,4 +24,16 @@ public void setName(String name) { public void setMovement(int movement) { this.movement = movement; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CarMovementDto that = (CarMovementDto) o; + return movement == that.movement && name.equals(that.name); + } } diff --git a/src/main/java/racingcar/model/dto/Race.java b/src/main/java/racingcar/model/dto/Race.java new file mode 100644 index 0000000..0b5aec5 --- /dev/null +++ b/src/main/java/racingcar/model/dto/Race.java @@ -0,0 +1,50 @@ +package racingcar.model.dto; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.function.Supplier; + +public class Race { + private int turn; + private final List cars; + + private Race() { + cars = new ArrayList<>(); + } + + public static Race init(List cars) { + Race race = new Race(); + race.cars.addAll(cars.stream().map(Car::new).toList()); + race.turn = 0; + return race; + } + + public void advance(Supplier movementFunction) { + cars.forEach((car) -> { + if (movementFunction.get()) { + car.move(); + } + }); + turn++; + } + + public List getResult() { + return cars.stream().map((Car::getData)).toList(); + } + + public List getWinner() { + int maxValue = cars.stream() + .map(car -> car.getData().getMovement()) + .max(Comparator.naturalOrder()).orElse(0); + + return cars.stream() + .filter(car -> car.getData().getMovement() == maxValue) + .map(car -> car.getData().getName()) + .toList(); + } + + public int getTurn() { + return turn; + } +} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index c8ba0dd..a4b91d6 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -14,9 +14,9 @@ public void printResultGuide() { public void printPlayerResult(List playerResult) { playerResult.forEach((result) -> - System.out.printf( + System.out.println(String.format( MessageConstants.OUTPUT_CAR_MOVEMENT.getMessage(), result.getName() - + MOVEMENT_DELIMITER.repeat(result.getMovement())) + ) + MOVEMENT_DELIMITER.repeat(result.getMovement())) ); } diff --git a/src/test/java/racingcar/MockRandomProvider.java b/src/test/java/racingcar/MockRandomProvider.java new file mode 100644 index 0000000..4c77af1 --- /dev/null +++ b/src/test/java/racingcar/MockRandomProvider.java @@ -0,0 +1,17 @@ +package racingcar; + +public class MockRandomProvider { + private final boolean[] randomStack; + private int index = 0; + + public MockRandomProvider(boolean... randomStack) { + this.randomStack = randomStack; + } + + public boolean mockingRandom() { + if (index <= randomStack.length) { + return randomStack[index++]; + } + throw new IllegalStateException(); + } +} diff --git a/src/test/java/racingcar/ModelTest.java b/src/test/java/racingcar/ModelTest.java new file mode 100644 index 0000000..9ddc973 --- /dev/null +++ b/src/test/java/racingcar/ModelTest.java @@ -0,0 +1,65 @@ +package racingcar; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.stream.IntStream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import racingcar.model.dto.CarMovementDto; +import racingcar.model.dto.Race; + +public class ModelTest { + private MockRandomProvider mockRandomProvider; + + @BeforeEach + void setUp() { + mockRandomProvider = new MockRandomProvider( + true, false, true, + true, true, true, + true, true, true, + true, true, true, + true, true, true + ); + } + + @Nested + @DisplayName("경주 테스트") + class RaceTest { + @Test + @DisplayName("초기화 테스트") + public void initialTest() { + Race race = Race.init(List.of("pobi", "woni", "jun")); + assertEquals(0, race.getTurn()); + assertEquals( + List.of( + new CarMovementDto("pobi", 0), + new CarMovementDto("woni", 0), + new CarMovementDto("jun", 0) + ), + race.getResult() + ); + } + + @Test + @DisplayName("이동 테스트") + public void movementTest() { + Race race = Race.init(List.of("pobi", "woni", "jun")); + IntStream.range(0, 5).forEach(i -> { + race.advance(mockRandomProvider::mockingRandom); + }); + assertEquals(5, race.getTurn()); + assertEquals( + List.of( + new CarMovementDto("pobi", 5), + new CarMovementDto("woni", 4), + new CarMovementDto("jun", 5) + ), + race.getResult() + ); + assertEquals(List.of("pobi", "jun"), race.getWinner()); + } + } +} diff --git a/src/test/java/racingcar/ViewTest.java b/src/test/java/racingcar/ViewTest.java index 7806808..877a356 100644 --- a/src/test/java/racingcar/ViewTest.java +++ b/src/test/java/racingcar/ViewTest.java @@ -1,29 +1,90 @@ package racingcar; +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import camp.nextstep.edu.missionutils.test.NsTest; +import java.util.List; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import racingcar.model.dto.CarMovementDto; import racingcar.view.InputView; +import racingcar.view.OutputView; -public class ViewTest { +public class ViewTest extends NsTest { private InputView inputView; + private OutputView outputView; @BeforeEach void setUp() { inputView = new InputView(new MockInputProvider("pobi,woni,jun", "5")); + outputView = new OutputView(); } - @Test - void carNameInputTest() { - String carNames = inputView.getCarName(); - assertEquals("pobi,woni,jun", carNames); + @Nested + @DisplayName("입력 테스트") + class InputTest { + @Test + @DisplayName("차 이름 테스트") + void carNameInputTest() { + String carNames = inputView.getCarName(); + assertEquals("pobi,woni,jun", carNames); + } + + @Test + @DisplayName("시도 횟수 테스트") + void trialInputTest() { + inputView.getCarName(); + int trialNumber = inputView.getTrialNumber(); + assertEquals(5, trialNumber); + } + } + + @Nested + @DisplayName("출력 테스트") + class OutputTest { + @Test + @DisplayName("결과 메시지 출력 테스트") + void printResultGuideTest() { + assertSimpleTest(() -> { + outputView.printResultGuide(); + assertThat(output()).contains("실행 결과"); + }); + } + + @Test + @DisplayName("경주 진행 출력 테스트") + void printPlayerResultTest() { + assertSimpleTest(() -> { + outputView.printPlayerResult( + List.of( + new CarMovementDto("pobi", 4), + new CarMovementDto("woni", 0), + new CarMovementDto("jun", 1) + ) + ); + assertThat(output()).isEqualTo("pobi : ----\nwoni : \njun : -"); + }); + } + + @ParameterizedTest + @DisplayName("우승자 출력 테스트") + @CsvSource({"pobi", "pobi, jun", "pobi, woni, jun"}) + void printWinnerTest(String winners) { + assertSimpleTest(() -> { + List winnerList = List.of(winners.split(", ")); + outputView.printWinner(winnerList); + assertThat(output()).isEqualTo("최종 우승자 : " + winners); + }); + } } - @Test - void trialInputTest() { - inputView.getCarName(); - int trialNumber = inputView.getTrialNumber(); - assertEquals(5, trialNumber); + @Override + public void runMain() { } } From 67119bbf43cd591b2411451900287e74e49a1ecf Mon Sep 17 00:00:00 2001 From: moongua404 Date: Wed, 12 Feb 2025 15:59:52 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat(controller):=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=EB=B0=8F=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 컨트롤러를 통해 뷰와 서비스를 이음 - 서비스를 통해 모델과 이음 - Appconfig를 통해 의존성 주입 - OutputView에 개행 메서드 추가 --- src/main/java/racingcar/AppConfig.java | 36 ++++++++++++++ src/main/java/racingcar/Application.java | 9 ++++ .../controller/RacingcarController.java | 49 +++++++++++++++++++ .../racingcar/service/CarControlService.java | 28 +++++++++++ .../java/racingcar/service/RandomService.java | 9 ++++ src/main/java/racingcar/view/OutputView.java | 4 ++ 6 files changed, 135 insertions(+) create mode 100644 src/main/java/racingcar/service/CarControlService.java create mode 100644 src/main/java/racingcar/service/RandomService.java diff --git a/src/main/java/racingcar/AppConfig.java b/src/main/java/racingcar/AppConfig.java index d71d934..d49c0f7 100644 --- a/src/main/java/racingcar/AppConfig.java +++ b/src/main/java/racingcar/AppConfig.java @@ -1,4 +1,40 @@ package racingcar; +import racingcar.controller.RacingcarController; +import racingcar.service.CarControlService; +import racingcar.service.RandomService; +import racingcar.view.InputView; +import racingcar.view.OutputView; +import racingcar.view.provider.InputProvider; +import racingcar.view.provider.WoowaInputProvider; + public class AppConfig { + public InputProvider inputProvider() { + return new WoowaInputProvider(); + } + + public InputView inputView() { + return new InputView(inputProvider()); + } + + public OutputView outputView() { + return new OutputView(); + } + + public CarControlService carControlService() { + return new CarControlService(); + } + + public RandomService randomService() { + return new RandomService(); + } + + public RacingcarController racingcarController() { + return new RacingcarController( + inputView(), + outputView(), + carControlService(), + randomService() + ); + } } diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e..51d3407 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,16 @@ package racingcar; +import racingcar.controller.RacingcarController; + public class Application { + static AppConfig appConfig; + static RacingcarController racingcarController; + public static void main(String[] args) { // TODO: 프로그램 구현 + appConfig = new AppConfig(); + racingcarController = appConfig.racingcarController(); + + racingcarController.run(); } } diff --git a/src/main/java/racingcar/controller/RacingcarController.java b/src/main/java/racingcar/controller/RacingcarController.java index 2f6b533..9f7ac7a 100644 --- a/src/main/java/racingcar/controller/RacingcarController.java +++ b/src/main/java/racingcar/controller/RacingcarController.java @@ -1,4 +1,53 @@ package racingcar.controller; +import java.util.List; +import java.util.stream.IntStream; +import racingcar.model.dto.CarMovementDto; +import racingcar.service.CarControlService; +import racingcar.service.RandomService; +import racingcar.view.InputView; +import racingcar.view.OutputView; + public class RacingcarController { + InputView inputView; + OutputView outputView; + CarControlService carControlService; + RandomService randomService; + + private int trials; + + public RacingcarController(InputView inputView, OutputView outputView, + CarControlService carControlService, RandomService randomService) { + this.inputView = inputView; + this.outputView = outputView; + this.carControlService = carControlService; + this.randomService = randomService; + } + + public void run() { + initialize(); + execute(); + getResult(); + } + + private void initialize() { + String names = inputView.getCarName(); + trials = inputView.getTrialNumber(); + carControlService.initialize(names); + outputView.newLine(); + } + + private void execute() { + outputView.printResultGuide(); + IntStream.range(0, trials).forEach((t) -> { + List progress = carControlService.playTurn(randomService::willCarMove); + outputView.printPlayerResult(progress); + outputView.newLine(); + }); + } + + private void getResult() { + List winner = carControlService.getWinner(); + outputView.printWinner(winner); + } } diff --git a/src/main/java/racingcar/service/CarControlService.java b/src/main/java/racingcar/service/CarControlService.java new file mode 100644 index 0000000..fd0b783 --- /dev/null +++ b/src/main/java/racingcar/service/CarControlService.java @@ -0,0 +1,28 @@ +package racingcar.service; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; +import racingcar.model.dto.CarMovementDto; +import racingcar.model.dto.Race; + +public class CarControlService { + private Race race; + + public void initialize(String names) { + this.race = Race.init(parseNames(names)); + } + + private List parseNames(String names) { + return Arrays.stream(names.trim().split(",")).toList(); + } + + public List playTurn(Supplier movementFunction) { + race.advance(movementFunction); + return race.getResult(); + } + + public List getWinner() { + return race.getWinner(); + } +} diff --git a/src/main/java/racingcar/service/RandomService.java b/src/main/java/racingcar/service/RandomService.java new file mode 100644 index 0000000..3ee9e51 --- /dev/null +++ b/src/main/java/racingcar/service/RandomService.java @@ -0,0 +1,9 @@ +package racingcar.service; + +import camp.nextstep.edu.missionutils.Randoms; + +public class RandomService { + public boolean willCarMove() { + return Randoms.pickNumberInRange(0, 9) >= 4; + } +} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index a4b91d6..0b625e8 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -26,4 +26,8 @@ public void printWinner(List names) { + String.join(SEPARATOR, names) ); } + + public void newLine() { + System.out.println(); + } } From 8c40bdf48c4c0d2d318009838baa6c941860f9ea Mon Sep 17 00:00:00 2001 From: moongua404 Date: Wed, 12 Feb 2025 16:07:42 +0900 Subject: [PATCH 6/8] =?UTF-8?q?style:=20=EB=A6=B0=ED=8A=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/model/{dto => }/Car.java | 4 ++- .../java/racingcar/model/{dto => }/Race.java | 9 ++++--- .../racingcar/model/dto/CarMovementDto.java | 27 ++----------------- .../racingcar/service/CarControlService.java | 2 +- src/main/java/racingcar/view/OutputView.java | 4 +-- src/test/java/racingcar/ApplicationTest.java | 21 +++++++-------- src/test/java/racingcar/ModelTest.java | 6 ++--- 7 files changed, 25 insertions(+), 48 deletions(-) rename src/main/java/racingcar/model/{dto => }/Car.java (81%) rename src/main/java/racingcar/model/{dto => }/Race.java (81%) diff --git a/src/main/java/racingcar/model/dto/Car.java b/src/main/java/racingcar/model/Car.java similarity index 81% rename from src/main/java/racingcar/model/dto/Car.java rename to src/main/java/racingcar/model/Car.java index 9637ecf..79eab81 100644 --- a/src/main/java/racingcar/model/dto/Car.java +++ b/src/main/java/racingcar/model/Car.java @@ -1,4 +1,6 @@ -package racingcar.model.dto; +package racingcar.model; + +import racingcar.model.dto.CarMovementDto; public class Car { private final String name; diff --git a/src/main/java/racingcar/model/dto/Race.java b/src/main/java/racingcar/model/Race.java similarity index 81% rename from src/main/java/racingcar/model/dto/Race.java rename to src/main/java/racingcar/model/Race.java index 0b5aec5..ee6b08e 100644 --- a/src/main/java/racingcar/model/dto/Race.java +++ b/src/main/java/racingcar/model/Race.java @@ -1,9 +1,10 @@ -package racingcar.model.dto; +package racingcar.model; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.function.Supplier; +import racingcar.model.dto.CarMovementDto; public class Race { private int turn; @@ -35,12 +36,12 @@ public List getResult() { public List getWinner() { int maxValue = cars.stream() - .map(car -> car.getData().getMovement()) + .map(car -> car.getData().movement()) .max(Comparator.naturalOrder()).orElse(0); return cars.stream() - .filter(car -> car.getData().getMovement() == maxValue) - .map(car -> car.getData().getName()) + .filter(car -> car.getData().movement() == maxValue) + .map(car -> car.getData().name()) .toList(); } diff --git a/src/main/java/racingcar/model/dto/CarMovementDto.java b/src/main/java/racingcar/model/dto/CarMovementDto.java index a99e4c9..31c8c18 100644 --- a/src/main/java/racingcar/model/dto/CarMovementDto.java +++ b/src/main/java/racingcar/model/dto/CarMovementDto.java @@ -1,30 +1,7 @@ package racingcar.model.dto; -public class CarMovementDto { - private String name; - private int movement; - - public CarMovementDto(String name, int movement) { - this.name = name; - this.movement = movement; - } - - public String getName() { - return name; - } - - public int getMovement() { - return movement; - } - - public void setName(String name) { - this.name = name; - } - - public void setMovement(int movement) { - this.movement = movement; - } - +public record CarMovementDto(String name, int movement) { + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/racingcar/service/CarControlService.java b/src/main/java/racingcar/service/CarControlService.java index fd0b783..35e31db 100644 --- a/src/main/java/racingcar/service/CarControlService.java +++ b/src/main/java/racingcar/service/CarControlService.java @@ -3,8 +3,8 @@ import java.util.Arrays; import java.util.List; import java.util.function.Supplier; +import racingcar.model.Race; import racingcar.model.dto.CarMovementDto; -import racingcar.model.dto.Race; public class CarControlService { private Race race; diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index 0b625e8..353df7f 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -15,8 +15,8 @@ public void printResultGuide() { public void printPlayerResult(List playerResult) { playerResult.forEach((result) -> System.out.println(String.format( - MessageConstants.OUTPUT_CAR_MOVEMENT.getMessage(), result.getName() - ) + MOVEMENT_DELIMITER.repeat(result.getMovement())) + MessageConstants.OUTPUT_CAR_MOVEMENT.getMessage(), result.name() + ) + MOVEMENT_DELIMITER.repeat(result.movement())) ); } diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index 1d35fc3..c9c039e 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -1,12 +1,10 @@ package racingcar; -import camp.nextstep.edu.missionutils.test.NsTest; -import org.junit.jupiter.api.Test; - import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInRangeTest; -import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import camp.nextstep.edu.missionutils.test.NsTest; +import org.junit.jupiter.api.Test; class ApplicationTest extends NsTest { private static final int MOVING_FORWARD = 4; @@ -15,14 +13,14 @@ class ApplicationTest extends NsTest { @Test void 기능_테스트() { assertRandomNumberInRangeTest( - () -> { - run("pobi,woni", "1"); - assertThat(output()).contains("pobi : -", "woni : ", "최종 우승자 : pobi"); - }, - MOVING_FORWARD, STOP + () -> { + run("pobi,woni", "1"); + assertThat(output()).contains("pobi : -", "woni : ", "최종 우승자 : pobi"); + }, + MOVING_FORWARD, STOP ); } - + /* @Test void 예외_테스트() { assertSimpleTest(() -> @@ -30,6 +28,7 @@ class ApplicationTest extends NsTest { .isInstanceOf(IllegalArgumentException.class) ); } + */ @Override public void runMain() { diff --git a/src/test/java/racingcar/ModelTest.java b/src/test/java/racingcar/ModelTest.java index 9ddc973..0bc759b 100644 --- a/src/test/java/racingcar/ModelTest.java +++ b/src/test/java/racingcar/ModelTest.java @@ -8,8 +8,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import racingcar.model.Race; import racingcar.model.dto.CarMovementDto; -import racingcar.model.dto.Race; public class ModelTest { private MockRandomProvider mockRandomProvider; @@ -47,9 +47,7 @@ public void initialTest() { @DisplayName("이동 테스트") public void movementTest() { Race race = Race.init(List.of("pobi", "woni", "jun")); - IntStream.range(0, 5).forEach(i -> { - race.advance(mockRandomProvider::mockingRandom); - }); + IntStream.range(0, 5).forEach(i -> race.advance(mockRandomProvider::mockingRandom)); assertEquals(5, race.getTurn()); assertEquals( List.of( From 0f4b8cfa27db3ac78c92903d8ef9ebdebfd99bba Mon Sep 17 00:00:00 2001 From: moongua404 Date: Thu, 13 Feb 2025 15:08:48 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이름 길이 유효성 검사 - 빈 입력 / 빈 이름 유효성 검사 - 중복된 이름 유효성 검사 - 시도 횟수 유효성 검사 --- .../controller/RacingcarController.java | 2 ++ .../racingcar/service/CarControlService.java | 13 +++++++++- src/main/java/racingcar/utils/Validator.java | 26 +++++++++++++++++++ src/test/java/racingcar/ApplicationTest.java | 11 ++++---- 4 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 src/main/java/racingcar/utils/Validator.java diff --git a/src/main/java/racingcar/controller/RacingcarController.java b/src/main/java/racingcar/controller/RacingcarController.java index 9f7ac7a..17e6b67 100644 --- a/src/main/java/racingcar/controller/RacingcarController.java +++ b/src/main/java/racingcar/controller/RacingcarController.java @@ -5,6 +5,7 @@ import racingcar.model.dto.CarMovementDto; import racingcar.service.CarControlService; import racingcar.service.RandomService; +import racingcar.utils.Validator; import racingcar.view.InputView; import racingcar.view.OutputView; @@ -33,6 +34,7 @@ public void run() { private void initialize() { String names = inputView.getCarName(); trials = inputView.getTrialNumber(); + Validator.naturalNumberCheck(trials); carControlService.initialize(names); outputView.newLine(); } diff --git a/src/main/java/racingcar/service/CarControlService.java b/src/main/java/racingcar/service/CarControlService.java index 35e31db..1fa4f2b 100644 --- a/src/main/java/racingcar/service/CarControlService.java +++ b/src/main/java/racingcar/service/CarControlService.java @@ -5,12 +5,18 @@ import java.util.function.Supplier; import racingcar.model.Race; import racingcar.model.dto.CarMovementDto; +import racingcar.utils.Validator; public class CarControlService { private Race race; + private static final int MIN_CHAR_LENGTH = 1; + private static final int MAX_CHAR_LENGTH = 5; + public void initialize(String names) { - this.race = Race.init(parseNames(names)); + List nameList = parseNames(names); + validateName(nameList); + this.race = Race.init(nameList); } private List parseNames(String names) { @@ -25,4 +31,9 @@ public List playTurn(Supplier movementFunction) { public List getWinner() { return race.getWinner(); } + + private void validateName(List nameList) { + nameList.forEach((name) -> Validator.stringRangeCheck(name, MIN_CHAR_LENGTH, MAX_CHAR_LENGTH)); + Validator.duplicationCheck(nameList); + } } diff --git a/src/main/java/racingcar/utils/Validator.java b/src/main/java/racingcar/utils/Validator.java new file mode 100644 index 0000000..eacbc0a --- /dev/null +++ b/src/main/java/racingcar/utils/Validator.java @@ -0,0 +1,26 @@ +package racingcar.utils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Validator { + public static void stringRangeCheck(String input, int min, int max) { + if (input.length() <= min || input.length() > max) { + throw new IllegalArgumentException("[Error] Invalid character length."); + } + } + + public static void duplicationCheck(List itemList) { + Set uniqueNames = new HashSet<>(itemList); + if (uniqueNames.size() != itemList.size()) { + throw new IllegalArgumentException("[Error] Duplicated."); + } + } + + public static void naturalNumberCheck(int num) { + if (num <= 0) { + throw new IllegalArgumentException("[Error] Invalid number."); + } + } +} diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index c9c039e..8181aca 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -1,7 +1,9 @@ package racingcar; import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInRangeTest; +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import camp.nextstep.edu.missionutils.test.NsTest; import org.junit.jupiter.api.Test; @@ -20,18 +22,17 @@ class ApplicationTest extends NsTest { MOVING_FORWARD, STOP ); } - /* + @Test void 예외_테스트() { assertSimpleTest(() -> - assertThatThrownBy(() -> runException("pobi,javaji", "1")) - .isInstanceOf(IllegalArgumentException.class) + assertThatThrownBy(() -> runException("pobi,javaji", "1")) + .isInstanceOf(IllegalArgumentException.class) ); } - */ @Override public void runMain() { Application.main(new String[]{}); } -} +} \ No newline at end of file From ef937aecdb752842396e1793ef4c4a57d80cd7b6 Mon Sep 17 00:00:00 2001 From: moongua404 Date: Thu, 13 Feb 2025 22:52:54 +0900 Subject: [PATCH 8/8] =?UTF-8?q?style:=20=EC=83=81=EC=88=98=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 숫자 및 구분자 상수 분리 --- .../racingcar/service/CarControlService.java | 3 +- .../java/racingcar/service/RandomService.java | 6 +- src/test/java/racingcar/ApplicationTest.java | 193 +++++++++++++++++- 3 files changed, 197 insertions(+), 5 deletions(-) diff --git a/src/main/java/racingcar/service/CarControlService.java b/src/main/java/racingcar/service/CarControlService.java index 1fa4f2b..f665b0e 100644 --- a/src/main/java/racingcar/service/CarControlService.java +++ b/src/main/java/racingcar/service/CarControlService.java @@ -12,6 +12,7 @@ public class CarControlService { private static final int MIN_CHAR_LENGTH = 1; private static final int MAX_CHAR_LENGTH = 5; + private static final String COMMA = ","; public void initialize(String names) { List nameList = parseNames(names); @@ -20,7 +21,7 @@ public void initialize(String names) { } private List parseNames(String names) { - return Arrays.stream(names.trim().split(",")).toList(); + return Arrays.stream(names.trim().split(COMMA)).toList(); } public List playTurn(Supplier movementFunction) { diff --git a/src/main/java/racingcar/service/RandomService.java b/src/main/java/racingcar/service/RandomService.java index 3ee9e51..ec2c613 100644 --- a/src/main/java/racingcar/service/RandomService.java +++ b/src/main/java/racingcar/service/RandomService.java @@ -3,7 +3,11 @@ import camp.nextstep.edu.missionutils.Randoms; public class RandomService { + private static final int LOWER_BOUND = 0; + private static final int UPPER_BOUND = 9; + private static final int THRESHOLD = 4; + public boolean willCarMove() { - return Randoms.pickNumberInRange(0, 9) >= 4; + return Randoms.pickNumberInRange(LOWER_BOUND, UPPER_BOUND) >= THRESHOLD; } } diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index 8181aca..f2c2495 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -3,9 +3,14 @@ import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInRangeTest; import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import camp.nextstep.edu.missionutils.test.NsTest; +import java.util.Arrays; +import java.util.Collections; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class ApplicationTest extends NsTest { @@ -17,7 +22,7 @@ class ApplicationTest extends NsTest { assertRandomNumberInRangeTest( () -> { run("pobi,woni", "1"); - assertThat(output()).contains("pobi : -", "woni : ", "최종 우승자 : pobi"); + assertThat(output().trim()).contains("pobi : -", "woni : ", "최종 우승자 : pobi"); }, MOVING_FORWARD, STOP ); @@ -31,8 +36,190 @@ class ApplicationTest extends NsTest { ); } + @Nested + @DisplayName("Level 1") + class Level1 { + @Test + @DisplayName("기본 기능 테스트") + void basicTest() { + Integer[] numbers = Stream.concat( + Stream.of(MOVING_FORWARD, STOP, STOP), + Collections.nCopies(12, MOVING_FORWARD).stream() + ).toArray(Integer[]::new); + assertRandomNumberInRangeTest( + () -> { + run("pobi,woni,jun", "5"); + assertThat(output().trim()).contains( + "pobi : -----", "woni : ----", "jun : ----", + "최종 우승자 : pobi" + ); + }, numbers[0], Arrays.copyOfRange(numbers, 1, numbers.length) + + ); + } + + @Test + @DisplayName("랜덤 범위 테스트") + void randomRangeTest() { + Integer[] numbers = {0, 9, 1, 8, 2, 7, 3, 6, 4, 5}; + assertRandomNumberInRangeTest( + () -> { + run("loser,winer", "5"); + assertThat(output().trim()).contains( + "loser : -", "winer : -----" + ); + assertThat(output().trim()).doesNotContain( + "loser : --", "winer : ------" + ); + }, numbers[0], Arrays.copyOfRange(numbers, 1, numbers.length) + ); + } + + @Test + @DisplayName("이름 길이 범위 테스트") + void longNameTest() { + assertSimpleTest(() -> { + assertThatThrownBy(() -> runException("java,script", "1")) + .isInstanceOf(IllegalArgumentException.class); + }); + } + } + + @Nested + @DisplayName("Level 2") + class Level2 { + @Test + @DisplayName("단일 우승자 테스트") + void singleWinnerTest() { + Integer[] numbers = {MOVING_FORWARD, STOP, STOP}; + assertRandomNumberInRangeTest( + () -> { + run("pobi,woni,jun", "1"); + assertThat(output().trim()).contains( + "최종 우승자 : pobi" + ); + }, numbers[0], Arrays.copyOfRange(numbers, 1, numbers.length) + + ); + } + + @Test + @DisplayName("공동 우승자(2명) 테스트") + void doubleWinnerTest() { + Integer[] numbers = {MOVING_FORWARD, MOVING_FORWARD, STOP}; + assertRandomNumberInRangeTest( + () -> { + run("pobi,woni,jun", "1"); + assertThat(output().trim()).contains( + "최종 우승자 : pobi, woni" + ); + }, numbers[0], Arrays.copyOfRange(numbers, 1, numbers.length) + + ); + } + + @Test + @DisplayName("공동 우승자(3명) 테스트") + void severalWinnerTest() { + Integer[] numbers = {MOVING_FORWARD, MOVING_FORWARD, MOVING_FORWARD}; + assertRandomNumberInRangeTest( + () -> { + run("pobi,woni,jun", "1"); + assertThat(output().trim()).contains( + "최종 우승자 : pobi, woni, jun" + ); + }, numbers[0], Arrays.copyOfRange(numbers, 1, numbers.length) + + ); + } + } + + @Nested + @DisplayName("Level 3") + class Level3 { + @Test + @DisplayName("많은 시도횟수 테스트") + void veryBigTrialsTest() { + Integer[] numbers = Collections.nCopies(200, MOVING_FORWARD).toArray(Integer[]::new); + + assertRandomNumberInRangeTest( + () -> { + run("hogun,park", "100"); + assertThat(output().trim()).contains( + "hogun : ----------------------------------------------------------------------------------------------------"); + assertThat(output().trim()).contains( + "park : ----------------------------------------------------------------------------------------------------"); + assertThat(output().trim()).doesNotContain( + "park : -----------------------------------------------------------------------------------------------------"); + assertThat(output().trim()).doesNotContain( + "park : -----------------------------------------------------------------------------------------------------"); + + assertThat(output().trim()).contains( + "최종 우승자 : hogun, park" + ); + }, numbers[0], Arrays.copyOfRange(numbers, 1, numbers.length) + ); + } + + @Test + @DisplayName("공백 이름 테스트") + void blankBetweenNameTest() { + Integer[] numbers = {MOVING_FORWARD, STOP}; + assertRandomNumberInRangeTest( + () -> { + run("ja va,ji gi", "1"); + assertThat(output().trim()).contains( + "최종 우승자 : ja va" + ); + }, numbers[0], Arrays.copyOfRange(numbers, 1, numbers.length) + + ); + } + + @Test + @DisplayName("빈 입력 테스트 테스트") + void emptyAnswerTest() { + assertThatThrownBy(() -> run("", "1")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("빈 이름 테스트") + void emptyNameTest() { + Integer[] numbers = Stream.concat( + Stream.of(STOP, MOVING_FORWARD, STOP), + Collections.nCopies(12, MOVING_FORWARD).stream() + ).toArray(Integer[]::new); + assertRandomNumberInRangeTest( + () -> { + try { + run("gamja,,yy", "1"); + assertThat(output().trim()).contains("최종 우승자 : yy"); + } catch (Exception exception) { + assertThat(exception).isInstanceOf(IllegalArgumentException.class); + } + }, numbers[0], Arrays.copyOfRange(numbers, 1, numbers.length) + + ); + } + + @Test + @DisplayName("음수 시도 횟수 테스트") + void minusTrialTest() { + assertThatThrownBy(() -> run("pobi,jun", "-1")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("이름 중복 테스트") + void duplicatedPlayerTest() { + assertThatThrownBy(() -> run("pobi,woni,pobi", "1")) + .isInstanceOf(IllegalArgumentException.class); + } + } + @Override public void runMain() { Application.main(new String[]{}); } -} \ No newline at end of file +}