From 476f1d54da4df08a41e82a711524697e85cb4206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A9=E1=84=89=E1=85=A6=E1=84=8B=E1=85=B3?= =?UTF-8?q?=E1=86=AB?= Date: Thu, 14 Nov 2024 18:28:46 +0900 Subject: [PATCH] FEAT racingcar first commit --- build.gradle | 3 + src/main/java/RacingGame.java | 33 ++++++++ src/main/java/core/Car.java | 49 ++++++++++++ src/main/java/core/CarConstraints.java | 12 +++ src/main/java/core/Race.java | 56 +++++++++++++ src/main/java/message/Message.java | 24 ++++++ src/main/java/utils/InputValidator.java | 65 +++++++++++++++ src/main/java/view/InputView.java | 80 +++++++++++++++++++ src/main/java/view/ResultView.java | 22 ++++++ src/test/java/RacingGameTest.java | 44 +++++++++++ src/test/java/core/CarConstraintsTest.java | 27 +++++++ src/test/java/core/CarTest.java | 47 +++++++++++ src/test/java/core/RaceTest.java | 47 +++++++++++ src/test/java/message/MessageTest.java | 13 +++ src/test/java/utils/InputValidatorTest.java | 58 ++++++++++++++ src/test/java/view/InputViewTest.java | 88 +++++++++++++++++++++ src/test/java/view/ResultViewTest.java | 55 +++++++++++++ 17 files changed, 723 insertions(+) create mode 100644 src/main/java/RacingGame.java create mode 100644 src/main/java/core/Car.java create mode 100644 src/main/java/core/CarConstraints.java create mode 100644 src/main/java/core/Race.java create mode 100644 src/main/java/message/Message.java create mode 100644 src/main/java/utils/InputValidator.java create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/ResultView.java create mode 100644 src/test/java/RacingGameTest.java create mode 100644 src/test/java/core/CarConstraintsTest.java create mode 100644 src/test/java/core/CarTest.java create mode 100644 src/test/java/core/RaceTest.java create mode 100644 src/test/java/message/MessageTest.java create mode 100644 src/test/java/utils/InputValidatorTest.java create mode 100644 src/test/java/view/InputViewTest.java create mode 100644 src/test/java/view/ResultViewTest.java diff --git a/build.gradle b/build.gradle index 8172fb7..09a427f 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,9 @@ repositories { } dependencies { + testImplementation 'org.mockito:mockito-inline:4.8.0' + testImplementation "org.mockito:mockito-junit-jupiter:3.9.0" + testImplementation "org.junit.jupiter:junit-jupiter:5.7.2" testImplementation "org.assertj:assertj-core:3.19.0" } diff --git a/src/main/java/RacingGame.java b/src/main/java/RacingGame.java new file mode 100644 index 0000000..c4a5b71 --- /dev/null +++ b/src/main/java/RacingGame.java @@ -0,0 +1,33 @@ +// RacingGame.java +import core.Car; +import core.Race; +import view.InputView; +import view.ResultView; + +import java.util.List; + +public class RacingGame { + public static void main(String[] args) { + try{ + List cars = InputView.getCars(); + int attempts = InputView.getAttempts(); + + Race race = new Race(cars, attempts); + System.out.println("실행 결과"); + + for (int i = 0; i < attempts; i++) { + race.start(); + ResultView.printRaceProgress(race.getCars()); + } + + List winners = race.getWinners(); + ResultView.printWinners(winners); + } + catch (Exception e){ + e.printStackTrace(); + System.exit(1); // Exit with a non-zero status to indicate an error + + } + + } +} diff --git a/src/main/java/core/Car.java b/src/main/java/core/Car.java new file mode 100644 index 0000000..cd92d0e --- /dev/null +++ b/src/main/java/core/Car.java @@ -0,0 +1,49 @@ +package core; + +import message.Message; +import utils.InputValidator; + +public class Car { + private final String name; + private int position; + + public Car(String name) { + String errorMessage = InputValidator.validateCarName(name); + if (errorMessage != null) { + throw new IllegalArgumentException(errorMessage); // 유효성 검사 실패시 예외 던짐 + } + this.name = name; + this.position = 0; + } + + // 새로운 생성자 (name과 position을 초기화할 수 있게 수정) + public Car(String name, int position) { + this.name = name; + this.position = position; + } + + + // 일정 거리 이상일 때만 위치를 1칸 증가시키는 메서드 + public void moveRandomly(int randomValue) { + if (randomValue >= CarConstraints.MIN_MOVE_VALUE) { + position++; + } + } + + // 주어진 거리만큼 위치를 이동시키는 메서드 + public void moveDistance(int distance) { + this.position += distance; + } + + public int getPosition() { + return position; + } + + public String getName() { + return name; + } + + public boolean isWinner(int maxPosition) { + return position == maxPosition; + } +} diff --git a/src/main/java/core/CarConstraints.java b/src/main/java/core/CarConstraints.java new file mode 100644 index 0000000..17e33d1 --- /dev/null +++ b/src/main/java/core/CarConstraints.java @@ -0,0 +1,12 @@ +package core; + +public class CarConstraints { + // 자동차 이름 최대 길이 + public static final int MAX_CAR_NAME_LENGTH = 5; + + // 자동차 이름 유효성 검사 정규식 + public static final String CAR_NAME_REGEX = "^[a-zA-Z]+[0-9]*$"; + + // 자동차 이동에 필요한 최소값 + public static final int MIN_MOVE_VALUE = 4; +} diff --git a/src/main/java/core/Race.java b/src/main/java/core/Race.java new file mode 100644 index 0000000..c2f4e04 --- /dev/null +++ b/src/main/java/core/Race.java @@ -0,0 +1,56 @@ +package core; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class Race { + private final List cars; + private final int attempts; + private final Random random; + + public Race(List cars, int attempts) { + this.cars = cars; + this.attempts = attempts; + this.random = new Random(); + } + + public void start() { + for (int i = 0; i < attempts; i++) { + raceOnce(); + } + } + + private void raceOnce() { + for (Car car : cars) { + // 이전에는 random.nextInt(10)을 그대로 사용했으나, + // 이제는 moveRandomly 메서드를 사용해 자동차의 위치를 이동시킨다. + car.moveRandomly(random.nextInt(10)); + } + } + + public List getWinners() { + int maxPosition = getMaxPosition(); + List winners = new ArrayList<>(); + for (Car car : cars) { + if (car.isWinner(maxPosition)) { + winners.add(car); + } + } + return winners; + } + + private int getMaxPosition() { + int maxPosition = 0; + for (Car car : cars) { + if (car.getPosition() > maxPosition) { + maxPosition = car.getPosition(); + } + } + return maxPosition; + } + + public List getCars() { + return cars; + } +} diff --git a/src/main/java/message/Message.java b/src/main/java/message/Message.java new file mode 100644 index 0000000..c4ae5a8 --- /dev/null +++ b/src/main/java/message/Message.java @@ -0,0 +1,24 @@ +package message; + +public enum Message { + CAR_NAME_PROMPT("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."), + CAR_NAME_INVALID_FORMAT("자동차 이름은 쉼표(,)로 구분해야 합니다."), + CAR_NAME_TOO_LONG("자동차 이름은 5자 이하여야 합니다."), + CAR_NAME_DUPLICATE("자동차 이름이 중복되었습니다. 다시 입력해주세요."), + ATTEMPT_PROMPT("시도할 회수는 몇회인가요?"), + INVALID_NUMBER("숫자만 입력 가능합니다. 다시 입력해주세요."), + CAR_NAME_INVALID("자동차 이름을 올바르게 입력하세요."), + MAX_ATTEMPTS_EXCEEDED("시도할 회수는 최대 10회까지 가능합니다. 다시 입력해주세요."); + + private final String message; + + // 생성자 + Message(String message) { + this.message = message; + } + + // 메시지를 반환하는 메서드 + public String getMessage() { + return message; + } +} diff --git a/src/main/java/utils/InputValidator.java b/src/main/java/utils/InputValidator.java new file mode 100644 index 0000000..7f070fa --- /dev/null +++ b/src/main/java/utils/InputValidator.java @@ -0,0 +1,65 @@ +package utils; + +import java.util.HashSet; +import java.util.Set; +import core.CarConstraints; +import message.Message; + +public class InputValidator { + + // 자동차 이름 유효성 검사 + public static String validateCarName(String name) { + if (name.length() > CarConstraints.MAX_CAR_NAME_LENGTH || !isValidCarName(name)) { + return Message.CAR_NAME_INVALID.getMessage(); + } + return null; // 유효한 이름이면 null 반환 + } + + + private static boolean isValidCarName(String name) { + return name.matches(CarConstraints.CAR_NAME_REGEX); + } + + // 자동차 이름들에 대한 유효성 검사 + public static String validateCarNames(String[] names) { + if (names.length < 2) { + return Message.CAR_NAME_INVALID_FORMAT.getMessage(); + } + + if (hasDuplicateName(names)) { + return Message.CAR_NAME_DUPLICATE.getMessage(); + } + + // 각 자동차 이름에 대해 유효성 검사 수행 + for (String name : names) { + String errorMessage = validateCarName(name.trim()); + if (errorMessage != null) { + return errorMessage; // 유효하지 않으면 에러 메시지 반환 + } + } + + return null; + } + + // 중복된 자동차 이름이 있는지 확인 + private static boolean hasDuplicateName(String[] names) { + Set uniqueNames = new HashSet<>(); + for (String name : names) { + String trimmedName = name.trim().toLowerCase(); + if (!uniqueNames.add(trimmedName)) { + return true; + } + } + return false; + } + + // 시도 횟수가 유효한지 확인 + public static boolean isValidAttempt(String input) { + try { + Integer.parseInt(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 0000000..ad15f33 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,80 @@ +package view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +import core.Car; +import message.Message; +import utils.InputValidator; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + + // 자동차 이름을 입력받는 메서드 + public static List getCars() { + printCarNamePrompt(); + return getValidCarNames(); + } + + private static List getValidCarNames() { + while (true) { // 반복문을 사용하여 유효한 입력을 받을 때까지 계속 요청 + String input = scanner.nextLine(); + String[] names = input.split(","); + + String errorMessage = InputValidator.validateCarNames(names); + if (errorMessage != null) { + printErrorMessage(errorMessage); + continue; // 유효하지 않으면 다시 입력 받기 + } + + return createCars(names); // 유효한 이름이 입력되면 반환 + } + } + + private static void printCarNamePrompt() { + System.out.println(Message.CAR_NAME_PROMPT.getMessage()); + } + + private static void printErrorMessage(String errorMessage) { + System.out.println(errorMessage); + } + + // 자동차 객체 리스트를 생성하는 메서드 + private static List createCars(String[] names) { + return Arrays.stream(names) + .map(String::trim) // 공백 제거 + .map(Car::new) // 각 이름으로 Car 객체 생성 + .toList(); + } + + // 시도 횟수를 입력받는 메서드 + public static int getAttempts() { + printAttemptPrompt(); + return getValidAttempt(); + } + + private static int getValidAttempt() { + while (true) { // 반복문으로 시도 횟수를 유효하게 받을 때까지 반복 + String input = scanner.nextLine(); + if (InputValidator.isValidAttempt(input)) { + return parseAttempt(input); + } + + printErrorMessage(Message.INVALID_NUMBER.getMessage()); + } + } + + private static void printAttemptPrompt() { + System.out.println(Message.ATTEMPT_PROMPT.getMessage()); + } + + private static int parseAttempt(String input) { + int attempts = Integer.parseInt(input); + if (attempts > 10) { + printErrorMessage(Message.MAX_ATTEMPTS_EXCEEDED.getMessage()); + return getAttempts(); // 시도 횟수 초과시 다시 입력 받기 + } + return attempts; + } +} diff --git a/src/main/java/view/ResultView.java b/src/main/java/view/ResultView.java new file mode 100644 index 0000000..3fc4bc2 --- /dev/null +++ b/src/main/java/view/ResultView.java @@ -0,0 +1,22 @@ +// view/ResultView.java +package view; + +import core.Car; +import java.util.List; + +public class ResultView { + public static void printRaceProgress(List cars) { + for (Car car : cars) { + System.out.print(car.getName() + " : "); + System.out.println("-".repeat(car.getPosition())); + } + System.out.println(); + } + + public static void printWinners(List winners) { + String winnerNames = String.join(", ", winners.stream() + .map(Car::getName) + .toList()); + System.out.println(winnerNames + "가 최종 우승했습니다."); + } +} diff --git a/src/test/java/RacingGameTest.java b/src/test/java/RacingGameTest.java new file mode 100644 index 0000000..cb683a6 --- /dev/null +++ b/src/test/java/RacingGameTest.java @@ -0,0 +1,44 @@ +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; // DisplayName 어노테이션을 import +import static org.junit.jupiter.api.Assertions.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +public class RacingGameTest { + + @Test + @DisplayName("주어진 3개의 자동차와 5번의 시도로 경주 진행 및 결과 출력 테스트") // 테스트 목적을 명확하게 설명 + public void testMainMethod() { + // Given: 자동차 이름 3개와 시도 횟수 5회에 대한 가짜 입력값 준비 + String input = "Car1,Car2,Car3\n5\n"; // Mock input: 3 cars and 5 attempts + + // System.in을 가짜 입력으로 변경 + System.setIn(new ByteArrayInputStream(input.getBytes())); + + // System.out을 캡처하기 위해 ByteArrayOutputStream 사용 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + + try { + // When: RacingGame의 main 메서드를 실행 + RacingGame.main(new String[]{}); + + // Then: 디스플레이 네임이 포함된 출력이 정상적으로 나오는지 확인 + // 출력 결과에 "Car1 : ", "Car2 : ", "Car3 : " 등이 포함되어 있어야 함 + String output = outputStream.toString(); + assertTrue(output.contains("Car1 : ")); + assertTrue(output.contains("Car2 : ")); + assertTrue(output.contains("Car3 : ")); + assertTrue(output.contains("가 최종 우승했습니다.")); // 최종 우승자가 출력되는지 확인 + } catch (Exception e) { + // 예외 발생 시 테스트 실패 + fail("Main method failed with exception: " + e.getMessage()); + } finally { + // 테스트 후 System.in과 System.out을 원래 상태로 복원 + System.setIn(System.in); + System.setOut(System.out); + } + } +} diff --git a/src/test/java/core/CarConstraintsTest.java b/src/test/java/core/CarConstraintsTest.java new file mode 100644 index 0000000..9c47c9e --- /dev/null +++ b/src/test/java/core/CarConstraintsTest.java @@ -0,0 +1,27 @@ +package core; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.DisplayName; + +class CarConstraintsTest { + + @Test + @DisplayName("최종 우승자 여부 확인") + void testCarConstraints() { + // Given: CarConstraints 클래스의 상수 값을 준비 + int maxCarNameLength = CarConstraints.MAX_CAR_NAME_LENGTH; + String carNameRegex = CarConstraints.CAR_NAME_REGEX; + int minMoveValue = CarConstraints.MIN_MOVE_VALUE; + + // When: 각 상수 값을 사용하여 제약 조건을 확인 + // - 자동차 이름 최대 길이는 5이어야 한다. + // - 자동차 이름은 알파벳으로 시작하고, 숫자만 뒤에 올 수 있어야 한다. + // - 자동차 이동에 필요한 최소 값은 4이어야 한다. + + // Then: 각 상수가 예상한 값을 가지고 있는지 확인 + assertEquals(5, maxCarNameLength, "자동차 이름 최대 길이는 5이어야 한다."); + assertEquals("^[a-zA-Z]+[0-9]*$", carNameRegex, "자동차 이름 유효성 검사 정규식은 알파벳으로 시작하고 숫자가 뒤따르는 형식이어야 한다."); + assertEquals(4, minMoveValue, "자동차 이동에 필요한 최소값은 4이어야 한다."); + } +} diff --git a/src/test/java/core/CarTest.java b/src/test/java/core/CarTest.java new file mode 100644 index 0000000..dcda412 --- /dev/null +++ b/src/test/java/core/CarTest.java @@ -0,0 +1,47 @@ +package core; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.DisplayName; + +class CarTest { + + @Test + @DisplayName("자동차가 정상적으로 이동하고 잘못된 이동은 무시되는지 확인") + void testCarMovement() { + // Given: "Car1"이라는 이름을 가진 새로운 자동차 객체를 생성 + Car car = new Car("Car1"); + + // When: 자동차가 랜덤값을 5로 받아 이동시키는 명령을 내림 (유효한 이동) + car.moveRandomly(5); // 유효한 이동 + + // Then: 자동차의 위치가 1로 업데이트 되어야 한다 (이동은 1칸씩만 진행됨) + assertEquals(1, car.getPosition()); // 자동차가 1칸 이동했으므로, 위치는 1이어야 한다. + + // When: 자동차가 랜덤값을 3으로 받아 이동시키는 명령을 내림 (잘못된 이동) + car.moveRandomly(3); // 잘못된 이동 + + // Then: 자동차의 위치는 여전히 1이어야 한다 (잘못된 이동은 무시됨) + assertEquals(1, car.getPosition()); // 자동차의 위치가 변경되지 않았으므로 여전히 1이어야 한다. + } + + @Test + @DisplayName("최종 우승자 여부 확인") + void testIsWinner() { + // Given: "Car1"이라는 이름을 가진 새로운 자동차 객체를 생성하고 5칸을 이동시킴 + Car car = new Car("Car1"); + car.moveDistance(5); // 위치 5로 이동 + + // When: 자동차가 최대 위치인 5에 도달했을 때 우승자인지 확인 + // Then: "Car1"은 우승자로 간주되어야 한다. + assertTrue(car.isWinner(5)); // 자동차의 위치가 최대 위치인 5에 도달하면 우승자로 간주 + + // Given: "Car1"을 다시 생성하고 3칸만 이동시킴 + car = new Car("Car1"); + car.moveDistance(3); // 위치 3으로 이동 + + // When: 자동차가 최대 위치인 5에 도달하지 않았을 때 우승자인지 확인 + // Then: "Car1"은 우승자가 아니어야 한다. + assertFalse(car.isWinner(5)); // 위치가 5에 미치지 않으면 우승자가 아니므로 false 반환 + } +} diff --git a/src/test/java/core/RaceTest.java b/src/test/java/core/RaceTest.java new file mode 100644 index 0000000..be51aaa --- /dev/null +++ b/src/test/java/core/RaceTest.java @@ -0,0 +1,47 @@ +package core; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.DisplayName; + +class RaceTest { + private Race race; + + @BeforeEach + void setUp() { + // Given: "Car1"과 "Car2"라는 이름을 가진 새로운 자동차 객체 두 개를 생성 + Car car1 = new Car("Car1"); + Car car2 = new Car("Car2"); + // 그리고 두 자동차를 5번의 시도로 경주를 시작할 수 있도록 Race 객체를 생성 + race = new Race(Arrays.asList(car1, car2), 5); // 5 attempts + } + + @Test + @DisplayName("경주 시작 후 자동차의 위치가 갱신되는지 확인") + void testStartRace() { + // Given: 5번의 시도로 경주를 시작 + // When: race.start() 메서드를 호출하여 경주를 시작 + race.start(); + + // Then: 자동차들의 위치가 0 이상으로 갱신되어야 한다 + assertTrue(race.getCars().get(0).getPosition() >= 0); // 첫 번째 자동차의 위치가 갱신되었는지 확인 + assertTrue(race.getCars().get(1).getPosition() >= 0); // 두 번째 자동차의 위치가 갱신되었는지 확인 + } + + @Test + @DisplayName("경주 후 우승자가 결정되는지 확인") + void testGetWinners() { + // Given: 5번의 시도로 경주를 시작 + race.start(); + + // When: 경주가 끝난 후 우승자 리스트를 가져옴 + List winners = race.getWinners(); + + // Then: 우승자 리스트가 null이 아니고, 적어도 하나의 우승자가 있어야 한다 + assertNotNull(winners); // 우승자 리스트가 null이 아님을 확인 + assertFalse(winners.isEmpty()); // 우승자가 하나 이상 있어야 함을 확인 + } +} diff --git a/src/test/java/message/MessageTest.java b/src/test/java/message/MessageTest.java new file mode 100644 index 0000000..c3e68cb --- /dev/null +++ b/src/test/java/message/MessageTest.java @@ -0,0 +1,13 @@ +package message; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class MessageTest { + + @Test + void testGetMessage() { + assertEquals("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).", Message.CAR_NAME_PROMPT.getMessage()); + assertEquals("자동차 이름은 5자 이하여야 합니다.", Message.CAR_NAME_TOO_LONG.getMessage()); + } +} diff --git a/src/test/java/utils/InputValidatorTest.java b/src/test/java/utils/InputValidatorTest.java new file mode 100644 index 0000000..5e6919f --- /dev/null +++ b/src/test/java/utils/InputValidatorTest.java @@ -0,0 +1,58 @@ +package utils; + +import message.Message; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.DisplayName; + +class InputValidatorTest { + + @Test + @DisplayName("최종 우승자 여부 확인") + void testValidateCarName() { + // Given: 유효한 자동차 이름과 유효하지 않은 자동차 이름들 + String validName = "Car1"; + String invalidName = "Car@123"; + + // When: validateCarName 메서드를 호출하여 각각의 이름을 검사 + String validResult = InputValidator.validateCarName(validName); + String invalidResult = InputValidator.validateCarName(invalidName); + + // Then: 유효한 이름은 null을 반환하고, 너무 긴 이름은 에러 메시지를 반환하며, 유효하지 않은 이름은 에러 메시지를 반환 + assertNull(validResult, "유효한 자동차 이름은 null을 반환해야 한다."); + assertEquals(Message.CAR_NAME_INVALID.getMessage(), invalidResult, "유효하지 않은 자동차 이름은 해당 에러 메시지를 반환해야 한다."); + } + + + @Test + @DisplayName("여러 자동차 이름에 대한 유효성 검사") + void testValidateCarNames() { + // Given: 여러 자동차 이름 배열을 준비 (유효한 이름, 중복된 이름, 너무 긴 이름) + String[] validNames = {"Car1", "Car2"}; + String[] duplicateNames = {"Car1", "Car1"}; // 중복된 이름 + + // When: validateCarNames 메서드를 호출하여 각 이름 배열을 검사 + String validNamesResult = InputValidator.validateCarNames(validNames); + String duplicateNamesResult = InputValidator.validateCarNames(duplicateNames); + + // Then: 유효한 이름 배열은 null을 반환하고, 중복된 이름 배열은 에러 메시지를 반환하며, 너무 긴 이름 배열은 에러 메시지를 반환 + assertNull(validNamesResult, "모든 자동차 이름이 유효하면 null을 반환해야 한다."); + assertEquals(Message.CAR_NAME_DUPLICATE.getMessage(), duplicateNamesResult, "중복된 이름이 있으면 해당 에러 메시지를 반환해야 한다."); + } + + @Test + @DisplayName("시도 횟수 유효성 검사") + void testIsValidAttempt() { + // Given: 유효한 숫자 문자열 "5"와 유효하지 않은 문자열 "invalid"를 준비 + String validAttempt = "5"; + String invalidAttempt = "invalid"; + + // When: isValidAttempt 메서드를 호출하여 각 입력 값의 유효성을 검사 + boolean validResult = InputValidator.isValidAttempt(validAttempt); + boolean invalidResult = InputValidator.isValidAttempt(invalidAttempt); + + // Then: 유효한 숫자 문자열은 true를 반환하고, 유효하지 않은 문자열은 false를 반환해야 한다 + assertTrue(validResult, "유효한 숫자 문자열은 true를 반환해야 한다."); + assertFalse(invalidResult, "유효하지 않은 문자열은 false를 반환해야 한다."); + } +} diff --git a/src/test/java/view/InputViewTest.java b/src/test/java/view/InputViewTest.java new file mode 100644 index 0000000..8fab1e9 --- /dev/null +++ b/src/test/java/view/InputViewTest.java @@ -0,0 +1,88 @@ +package view; + +import core.Car; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import utils.InputValidator; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +@DisplayName("최종 우승자 여부 확인") +public class InputViewTest { + + @Test + @DisplayName("유효한 자동차 이름 입력 테스트") + void testGetCars_ValidNames() { + // Given: 유효한 자동차 이름이 입력됨 + String validInput = "Car1, Car2, Car3\n"; + InputStream in = new ByteArrayInputStream(validInput.getBytes()); + System.setIn(in); + + // Mock InputValidator to validate names without errors + try (MockedStatic validatorMock = mockStatic(InputValidator.class)) { + validatorMock.when(() -> InputValidator.validateCarNames(any(String[].class))) + .thenReturn(null); + + // When: getCars 메서드가 호출됨 + List cars = InputView.getCars(); + + // Then: 유효한 이름으로 Car 객체가 생성되어야 함 + assertEquals(3, cars.size(), "자동차 이름 입력으로 3개의 Car 객체가 생성되어야 한다."); + assertEquals("Car1", cars.get(0).getName(), "첫 번째 자동차 이름은 Car1 이어야 한다."); + assertEquals("Car2", cars.get(1).getName(), "두 번째 자동차 이름은 Car2 이어야 한다."); + assertEquals("Car3", cars.get(2).getName(), "세 번째 자동차 이름은 Car3 이어야 한다."); + } + } + + @Test + @DisplayName("유효한 시도 횟수 입력 테스트") + void testGetAttempts_ValidNumber() { + // Given: 유효한 시도 횟수가 입력됨 + String validInput = "5\n"; + InputStream in = new ByteArrayInputStream(validInput.getBytes()); + System.setIn(in); + + // Mock InputValidator to validate attempt number without errors + try (MockedStatic validatorMock = mockStatic(InputValidator.class)) { + validatorMock.when(() -> InputValidator.isValidAttempt(any(String.class))) + .thenReturn(true); + + // When: getAttempts 메서드가 호출됨 + int attempts = InputView.getAttempts(); + + // Then: 유효한 시도 횟수가 반환되어야 함 + assertEquals(5, attempts, "유효한 시도 횟수 입력으로 5가 반환되어야 한다."); + } + } + + @Test + @DisplayName("시도 횟수가 최대값을 초과했을 때 다시 입력 요청 테스트") + void testGetAttempts_MaxExceeded() { + // Given: 시도 횟수가 최대값을 초과함 + String inputExceedingMax = "15\n5\n"; + InputStream in = new ByteArrayInputStream(inputExceedingMax.getBytes()); + System.setIn(in); + + // Mock InputValidator to validate attempt number + try (MockedStatic validatorMock = mockStatic(InputValidator.class)) { + validatorMock.when(() -> InputValidator.isValidAttempt(any(String.class))) + .thenReturn(true); + + // When: getAttempts 메서드가 호출됨 + int attempts = InputView.getAttempts(); + + // Then: 유효한 시도 횟수가 반환되어야 함 + assertEquals(5, attempts, "최대값을 초과한 입력 후 유효한 시도 횟수 5가 반환되어야 한다."); + } + } +} diff --git a/src/test/java/view/ResultViewTest.java b/src/test/java/view/ResultViewTest.java new file mode 100644 index 0000000..87065ff --- /dev/null +++ b/src/test/java/view/ResultViewTest.java @@ -0,0 +1,55 @@ +package view; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import core.Car; + +public class ResultViewTest { + + @Test + @DisplayName("경주 진행 상태 출력 테스트") // 경주 진행 상태 출력에 대한 테스트 + public void testPrintRaceProgress() { + // Given: 3개의 자동차와 각 자동차의 위치를 설정 + List cars = new ArrayList<>(); + cars.add(new Car("Car1", 3)); + cars.add(new Car("Car2", 5)); + cars.add(new Car("Car3", 2)); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + + // When: printRaceProgress 메서드를 실행 + ResultView.printRaceProgress(cars); + + // Then: 각 자동차의 이름과 해당하는 '-'가 출력되는지 확인 + String output = outputStream.toString(); + assertTrue(output.contains("Car1 : ---")); + assertTrue(output.contains("Car2 : -----")); + assertTrue(output.contains("Car3 : --")); + } + + @Test + @DisplayName("우승자 출력 테스트") // 우승자 출력에 대한 테스트 + public void testPrintWinners() { + // Given: 2명의 우승자를 설정 + List winners = new ArrayList<>(); + winners.add(new Car("Car1", 5)); + winners.add(new Car("Car2", 5)); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + + // When: printWinners 메서드를 실행 + ResultView.printWinners(winners); + + // Then: 우승자의 이름이 "Car1, Car2가 최종 우승했습니다." 형식으로 출력되는지 확인 + String output = outputStream.toString(); + assertTrue(output.contains("Car1, Car2가 최종 우승했습니다.")); + } +}