diff --git a/README.md b/README.md index d0286c859f..27b35cdde7 100644 --- a/README.md +++ b/README.md @@ -1 +1,117 @@ # java-racingcar-precourse + +## 기능 목록 + +### 1. 입력 +1.1 경주할 자동차 이름 입력 +- "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)" 문구 출력 후 문자열 입력 +- 예: `pobi,woni,jun` +- 쉼표(,) 기준 분리 +- 각 이름 앞뒤 공백 제거 + +1.2 이동 횟수 입력 +- "시도할 횟수는 몇 회인가요?" 문구 출력 후 문자열 입력 +- 양의 정수 기대 + + +### 2. 입력값 검증 +잘못된 입력값일 경우 `IllegalArgumentException` 발생 후 프로그램 종료 + +2.1 자동차 이름 검증 +- 빈 이름 금지 +- 공백만 있는 이름 금지 +- 이름 길이 5자 이하 + +2.2 이동 횟수 검증 +- 정수 변환 불가능한 입력값 예외 처리 +- 1 미만 값 예외 처리 + + +### 3. 경주 로직 + +3.1 자동차 초기화 +- 검증 통과한 이름 목록으로 자동차 객체 생성 +- 각 자동차는 이름과 현재 위치(position) 보유 +- 초기 position 값 0 + +3.2 단일 라운드 진행 +- 각 자동차마다 0부터 9 사이 무작위 정수 생성 +- 생성된 값이 4 이상이면 position 1 증가 +- 4 미만이면 이동 없음 + +3.3 전체 레이스 +- 입력된 시도 횟수만큼 라운드 반복 +- 매 라운드 종료 시 현재 상태를 출력 대상으로 준비 + + +### 4. 출력 + +4.1 라운드별 현황 출력 +- 한 라운드마다 모든 자동차 상태 출력 +- 형식: `이름 : 현재 position 수만큼 '-'` + - 예: `pobi : ---` +- 라운드마다 전체 출력 후 빈 줄 출력 + +4.2 최종 우승자 출력 +- 모든 라운드 종료 후 최대로 전진한 자동차를 우승자로 판단 +- 공동 우승 가능 +- 여러 우승자일 경우 쉼표(,) 연결 + - 예: `최종 우승자 : pobi, jun` +- 우승자 한 명일 경우 단일 이름만 출력 + + +## 구조 계획 +OOP, MVC, TDD 개념을 최대한 고려하여 설계 + +### Application +- `Application.main()`에서 프로그램 시작 +- 컨트롤러를 한 번 호출해서 전체 게임 진행 + +### GameController +- 전체 흐름 조립 책임 +- 입력 요청 → 검증 → 라운드 반복 진행 → 라운드별 결과 출력 → 최종 우승자 출력 +- 비즈니스 로직(경주 규칙)과 입출력 로직을 연결하는 계층 역할 + +### InputView / OutputView +- InputView + - 안내 문구 출력 후 `Console.readLine()`으로 입력 수집 + - 가공하지 않은 문자열 반환 +- OutputView + - 라운드별 현황 출력 + - 최종 우승자 출력 + - 출력 형식 고정 +- View 계층은 경주 로직을 모르게 유지. 출력만 수행 + +### Validator +- 자동차 이름 유효성 검증 + - 빈 문자열 금지 + - 공백만 있는 문자열 금지 + - 5자 초과 금지 +- 시도 횟수 유효성 검증 + - 정수 여부 + - 1 이상 여부 +- 위반 시 `IllegalArgumentException` 발생 +- 입력(InputView)과 검증(Validator)을 분리해 단일 책임 유지(SRP) + +### Domain / Service +- Car + - 자동차 이름과 현재 위치 필드 + - 이동 가능 여부를 받아 position 증가 여부 결정 +- Cars + - 자동차 여러 대를 관리 + - 라운드 단위 이동 처리 대상 +- RacingGameService + - 한 라운드 진행 + - 각 자동차마다 난수 생성 후 이동 여부 결정 + - 최종 우승자 계산 + - 최댓값 위치를 기준으로 우승 후보 목록 생성 +- Service와 Domain은 View에 의존하지 않도록 설계 + - JUnit5, AssertJ 단위 테스트 작성 대상 + +### 테스트 계획 +- Car 이동 규칙 테스트 + - 임의의 숫자 주입 후 position 증가 여부 확인 +- 우승자 계산 테스트 + - 여러 자동차 중 최댓값 위치를 가진 자동차 목록 계산 결과 확인 +- 검증 로직 테스트 + - 잘못된 이름/잘못된 횟수 입력 시 `IllegalArgumentException` 처리 여부 확인 \ No newline at end of file diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e724..697b0e51d9 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,10 @@ package racingcar; +import racingcar.controller.GameController; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + GameController controller = new GameController(); + controller.run(); } -} +} \ No newline at end of file diff --git a/src/main/java/racingcar/controller/GameController.java b/src/main/java/racingcar/controller/GameController.java new file mode 100644 index 0000000000..dd17fa219f --- /dev/null +++ b/src/main/java/racingcar/controller/GameController.java @@ -0,0 +1,54 @@ +package racingcar.controller; + +import racingcar.domain.Car; +import racingcar.domain.Cars; +import racingcar.service.RacingGameService; +import racingcar.view.InputView; +import racingcar.view.OutputView; + +import java.util.ArrayList; +import java.util.List; + +public class GameController { + + private final InputView inputView; + private final OutputView outputView; + private final RacingGameService racingGameService; + + public GameController() { + this.inputView = new InputView(); + this.outputView = new OutputView(); + this.racingGameService = new RacingGameService(); + } + + public void run() { + // 1. 입력 + List carNames = inputView.inputCarNames(); + int attemptCount = inputView.inputAttemptCount(); + + // 2. Cars 생성 + Cars cars = createCars(carNames); + + // 3. 실행 결과 헤더 출력 + System.out.println(); + System.out.println("실행 결과"); + + // 4. 시도 횟수만큼 라운드 진행 + for (int i = 0; i < attemptCount; i++) { + racingGameService.proceedRound(cars); + outputView.printRoundResult(cars); + } + + // 5. 최종 우승자 출력 + List winners = racingGameService.getWinners(cars); + outputView.printWinners(winners); + } + + private Cars createCars(List names) { + List list = new ArrayList<>(); + for (String name : names) { + list.add(new Car(name)); + } + return new Cars(list); + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java new file mode 100644 index 0000000000..4c1fa40a62 --- /dev/null +++ b/src/main/java/racingcar/domain/Car.java @@ -0,0 +1,27 @@ +package racingcar.domain; + +public class Car { + private static final int MOVE_THRESHOLD = 4; + + private final String name; + private int position; + + public Car(String name) { + this.name = name; + this.position = 0; + } + + public void moveIfPossible(int number) { + if (number >= MOVE_THRESHOLD) { + position++; + } + } + + public int getPosition() { + return position; + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java new file mode 100644 index 0000000000..11f867e811 --- /dev/null +++ b/src/main/java/racingcar/domain/Cars.java @@ -0,0 +1,27 @@ +package racingcar.domain; + +import java.util.Collections; +import java.util.List; + +public class Cars { + + private final List cars; + + public Cars(List cars) { + this.cars = cars; + } + + // 한 라운드에서 각 자동차가 받을 난수 리스트를 받아서 이동 시도 + public void moveAllWith(List numbers) { + for (int i = 0; i < cars.size(); i++) { + Car car = cars.get(i); + int number = numbers.get(i); + car.moveIfPossible(number); + } + } + + // 현재 상태를 조회할 수 있도록 읽기 전용 조회 제공 + public List asList() { + return Collections.unmodifiableList(cars); + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java new file mode 100644 index 0000000000..d1d76f128e --- /dev/null +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -0,0 +1,38 @@ +package racingcar.service; + +import camp.nextstep.edu.missionutils.Randoms; +import racingcar.domain.Car; +import racingcar.domain.Cars; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class RacingGameService { + // 라운드 1회 실행 + public void proceedRound(Cars cars) { + List numbers = new ArrayList<>(); + + for (int i = 0; i < cars.asList().size(); i++) { + int randomNumber = Randoms.pickNumberInRange(0, 9); + numbers.add(randomNumber); + } + + cars.moveAllWith(numbers); + } + + // 우승자 계산 + public List getWinners(Cars cars) { + // 이동거리 최대값 찾기 + int max = cars.asList().stream() + .mapToInt(Car::getPosition) + .max() + .orElse(0); + + // 이동거리 최대값을 가지는 자동차 모두 반환 + return cars.asList().stream() + .filter(car -> car.getPosition() == max) + .map(Car::getName) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/validator/AttemptCountValidator.java b/src/main/java/racingcar/validator/AttemptCountValidator.java new file mode 100644 index 0000000000..a33e534b8b --- /dev/null +++ b/src/main/java/racingcar/validator/AttemptCountValidator.java @@ -0,0 +1,23 @@ +package racingcar.validator; + +public class AttemptCountValidator { + + private AttemptCountValidator() { + } + + public static void validate(String rawCount) { + int count = parseInt(rawCount); + + if (count < 1) { + throw new IllegalArgumentException("시도 횟수는 1 이상의 정수여야 합니다."); + } + } + + private static int parseInt(String rawCount) { + try { + return Integer.parseInt(rawCount.trim()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("시도 횟수는 숫자여야 합니다."); + } + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/validator/CarNameValidator.java b/src/main/java/racingcar/validator/CarNameValidator.java new file mode 100644 index 0000000000..37e57383cc --- /dev/null +++ b/src/main/java/racingcar/validator/CarNameValidator.java @@ -0,0 +1,25 @@ +package racingcar.validator; + +public class CarNameValidator { + + private static final int MAX_NAME_LENGTH = 5; + + private CarNameValidator() { + } + + public static void validate(String rawName) { + if (rawName == null) { + throw new IllegalArgumentException("자동차 이름은 null일 수 없습니다."); + } + + String name = rawName.replaceAll(" ", ""); + + if (name.isEmpty()) { + throw new IllegalArgumentException("자동차 이름이 비어 있을 수 없습니다."); + } + + if (name.length() > MAX_NAME_LENGTH) { + throw new IllegalArgumentException("자동차 이름은 5자 이하여야 합니다."); + } + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java new file mode 100644 index 0000000000..ff7762c29c --- /dev/null +++ b/src/main/java/racingcar/view/InputView.java @@ -0,0 +1,43 @@ +package racingcar.view; + +import camp.nextstep.edu.missionutils.Console; +import racingcar.validator.CarNameValidator; +import racingcar.validator.AttemptCountValidator; + +import java.util.ArrayList; +import java.util.List; + +public class InputView { + + public List inputCarNames() { + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + String rawCarNames = Console.readLine(); + return parseCarNames(rawCarNames); + } + + public int inputAttemptCount() { + System.out.println("시도할 횟수는 몇 회인가요?"); + String rawAttemptCount = Console.readLine(); + return parseAttemptCount(rawAttemptCount); + } + + public static List parseCarNames(String rawCarNames) { + String[] tokens = rawCarNames.split(","); + List parsedCarList = new ArrayList<>(); + + for (String token : tokens) { + String name = token.trim(); + + // 자동차 이름 검증 (빈 문자열, 공백, 5자 초과 여부) + CarNameValidator.validate(name); + parsedCarList.add(name); + } + return parsedCarList; + } + + public static int parseAttemptCount(String rawAttemptCount) { + // 정수, 1 이상 여부 검증 + AttemptCountValidator.validate(rawAttemptCount); + return Integer.parseInt(rawAttemptCount.trim()); + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java new file mode 100644 index 0000000000..cf1e1345cf --- /dev/null +++ b/src/main/java/racingcar/view/OutputView.java @@ -0,0 +1,40 @@ +package racingcar.view; + +import racingcar.domain.Car; +import racingcar.domain.Cars; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; + +public class OutputView { + + // 한 라운드가 끝난 직후 상태 출력 + public void printRoundResult(Cars cars) { + for (String line : formatRoundResult(cars)) { + System.out.println(line); + } + System.out.println(); + } + + // 최종 우승자 출력 + public void printWinners(List winners) { + System.out.println(formatWinners(winners)); + } + + // 라운드 결과 형식 가공 + public static List formatRoundResult(Cars cars) { + List lines = new ArrayList<>(); + for (Car car : cars.asList()) { + String hyphens = "-".repeat(car.getPosition()); + lines.add(car.getName() + " : " + hyphens); + } + return lines; + } + + // 최종 우승자 형식 가공 + public static String formatWinners(List winners) { + String joinedNames = String.join(", ", winners); + return "최종 우승자 : " + joinedNames; + } +} \ No newline at end of file diff --git a/src/test/java/racingcar/domain/CarTest.java b/src/test/java/racingcar/domain/CarTest.java new file mode 100644 index 0000000000..2801878208 --- /dev/null +++ b/src/test/java/racingcar/domain/CarTest.java @@ -0,0 +1,29 @@ +package racingcar.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CarTest { + + @DisplayName("값이 4 이상이면 자동차 위치가 1 증가한다") + @Test + void 값이_4_이상이면_전진() { + Car car = new Car("pobi"); // 시작 position = 0 가정 + + car.moveIfPossible(4); // 전진 조건 충족 + + assertThat(car.getPosition()).isEqualTo(1); + } + + @DisplayName("값이 3 이하이면 자동차 위치가 증가하지 않는다") + @Test + void 값이_4_미만이면_그대로() { + Car car = new Car("pobi"); + + car.moveIfPossible(3); // 전진 조건 불충족 + + assertThat(car.getPosition()).isEqualTo(0); + } +} \ No newline at end of file diff --git a/src/test/java/racingcar/domain/CarsTest.java b/src/test/java/racingcar/domain/CarsTest.java new file mode 100644 index 0000000000..fb146a80ca --- /dev/null +++ b/src/test/java/racingcar/domain/CarsTest.java @@ -0,0 +1,38 @@ +package racingcar.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class CarsTest { + + @DisplayName("각 자동차에 대해 주어진 숫자 리스트를 적용해 전진 여부를 결정한다") + @Test + void 각_자동차_전진여부_결정() { + Car pobi = new Car("pobi"); + Car woni = new Car("woni"); + Car jun = new Car("jun"); + + Cars cars = new Cars(List.of(pobi, jun, woni)); + cars.moveAllWith(List.of(4, 3, 9)); + + assertThat(pobi.getPosition()).isEqualTo(1); + assertThat(woni.getPosition()).isEqualTo(1); + assertThat(jun.getPosition()).isEqualTo(0); + } + + @DisplayName("Cars는 내부 Car 목록을 외부에서 읽을 수 있게 제공한다 (읽기 전용)") + @Test + void Car_목록_외부에서_읽기() { + Car pobi = new Car("pobi"); + Car jun = new Car("jun"); + + Cars cars = new Cars(List.of(pobi, jun)); + + List view = cars.asList(); + assertThat(view).containsExactly(pobi, jun); + } +} \ No newline at end of file diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java new file mode 100644 index 0000000000..b21f2a7bfa --- /dev/null +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -0,0 +1,70 @@ +package racingcar.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racingcar.domain.Car; +import racingcar.domain.Cars; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class RacingGameServiceTest { + + @DisplayName("가장 멀리 간 자동차가 한 대면 그 자동차만 우승자로 반환") + @Test + void 단독_우승() { + // given + Car pobi = new Car("pobi"); + Car woni = new Car("woni"); + + // pobi 이동 2칸 + pobi.moveIfPossible(4); + pobi.moveIfPossible(7); + + // jun 이동 1칸 + woni.moveIfPossible(9); + + Cars cars = new Cars(List.of(pobi, woni)); + + RacingGameService service = new RacingGameService(); + + // when + List winners = service.getWinners(cars); + + // then + // pobi만 최대 position이므로 pobi만 우승 + assertThat(winners).containsExactly("pobi"); + } + + @DisplayName("가장 멀리 간 자동차가 여러 대면 공동 우승 전부 반환") + @Test + void 공동_우승() { + // given + Car pobi = new Car("pobi"); + Car woni = new Car("woni"); + Car jun = new Car("jun"); + + // pobi 2칸 + pobi.moveIfPossible(5); + pobi.moveIfPossible(9); + + // woni 1칸 + woni.moveIfPossible(6); + + // jun 2칸 + jun.moveIfPossible(8); + jun.moveIfPossible(4); + + Cars cars = new Cars(List.of(pobi, woni, jun)); + + RacingGameService service = new RacingGameService(); + + // when + List winners = service.getWinners(cars); + + // then + // pobi, jun은 최대 position으로 동일 + assertThat(winners).containsExactlyInAnyOrder("pobi", "jun"); + } +} \ No newline at end of file diff --git a/src/test/java/racingcar/validator/AttemptCountValidatorTest.java b/src/test/java/racingcar/validator/AttemptCountValidatorTest.java new file mode 100644 index 0000000000..57ac0c796d --- /dev/null +++ b/src/test/java/racingcar/validator/AttemptCountValidatorTest.java @@ -0,0 +1,34 @@ +package racingcar.validator; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class AttemptCountValidatorTest { + + @DisplayName("1 이상의 정수 문자열이면 통과") + @Test + void 정수이고_1이상이면_통과() { + assertThatCode(() -> AttemptCountValidator.validate("5")) + .doesNotThrowAnyException(); + } + + @DisplayName("숫자가 아니면 예외 발생") + @Test + void 숫자가_아니면_예외() { + assertThatThrownBy(() -> AttemptCountValidator.validate("aaa")) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("0 이하 숫자면 예외 발생") + @ParameterizedTest + @ValueSource(strings = {"0", "0.1", "-3", "-10"}) + void 정수지만_0이하면_예외(String input) { + assertThatThrownBy(() -> AttemptCountValidator.validate(input)) + .isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file diff --git a/src/test/java/racingcar/validator/CarNameValidatorTest.java b/src/test/java/racingcar/validator/CarNameValidatorTest.java new file mode 100644 index 0000000000..e65ee8ff49 --- /dev/null +++ b/src/test/java/racingcar/validator/CarNameValidatorTest.java @@ -0,0 +1,38 @@ +package racingcar.validator; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class CarNameValidatorTest { + + @DisplayName("유효한 이름(비어있지 않고, 길이 5자 이하)은 통과") + @Test + void 유효한_이름_통과() { + assertThatCode(() -> CarNameValidator.validate("pobi")) + .doesNotThrowAnyException(); + } + + @DisplayName("빈 문자열이면 예외 발생") + @Test + void 빈_문자열_예외() { + assertThatThrownBy(() -> CarNameValidator.validate("")) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("공백만 있는 이름이면 예외 발생") + @Test + void 공백만_있는_문자열_예외() { + assertThatThrownBy(() -> CarNameValidator.validate(" ")) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("6자 이상 이름이면 예외 발생") + @Test + void 이름_5자_초과_예외() { + assertThatThrownBy(() -> CarNameValidator.validate("abcdef")) // 6글자 + .isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file diff --git a/src/test/java/racingcar/view/InputViewTest.java b/src/test/java/racingcar/view/InputViewTest.java new file mode 100644 index 0000000000..7ec43b2e58 --- /dev/null +++ b/src/test/java/racingcar/view/InputViewTest.java @@ -0,0 +1,53 @@ +package racingcar.view; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class InputViewTest { + + @DisplayName("쉼표로 구분된 자동차 이름 문자열을 trim 후 리스트로 파싱") + @Test + void 자동차_이름_리스트로_파싱() { + List names = InputView.parseCarNames("pobi, woni, jun"); + + assertThat(names).containsExactly("pobi", "woni", "jun"); + } + + @DisplayName("자동차 이름이 비었거나 5자를 초과하면 예외 발생") + @Test + void 유효한_자동차_이름이_아니면_예외() { + // 빈 이름 + assertThatThrownBy(() -> InputView.parseCarNames(",pobi")) + .isInstanceOf(IllegalArgumentException.class); + + // 공백 이름 + assertThatThrownBy(() -> InputView.parseCarNames(" ,pobi")) + .isInstanceOf(IllegalArgumentException.class); + + // 5자를 넘는 이름 + assertThatThrownBy(() -> InputView.parseCarNames("pobi, aaaaaa")) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("시도 횟수 문자열을 정수로 변환") + @Test + void 시도_횟수_정수_확인() { + int count = InputView.parseAttemptCount("5"); + assertThat(count).isEqualTo(5); + } + + @DisplayName("시도 횟수가 숫자가 아니거나 1 미만이면 예외 발생") + @Test + void 유효한_시도_횟수가_아니면_예외() { + assertThatThrownBy(() -> InputView.parseAttemptCount("aaa")) + .isInstanceOf(IllegalArgumentException.class); + + assertThatThrownBy(() -> InputView.parseAttemptCount("0")) + .isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file diff --git a/src/test/java/racingcar/view/OutputViewTest.java b/src/test/java/racingcar/view/OutputViewTest.java new file mode 100644 index 0000000000..8c75a1007a --- /dev/null +++ b/src/test/java/racingcar/view/OutputViewTest.java @@ -0,0 +1,59 @@ +package racingcar.view; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racingcar.domain.Car; +import racingcar.domain.Cars; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class OutputViewTest { + + @DisplayName("라운드 결과 문자열은 '이름 : ---' 형식으로 생성되어야 한다") + @Test + void 라운드_결과_문자열_포맷_테스트() { + // given + Car pobi = new Car("pobi"); + Car woni = new Car("woni"); + + pobi.moveIfPossible(4); // pobi -> 1 + pobi.moveIfPossible(9); // pobi -> 2 + woni.moveIfPossible(7); // woni -> 1 + + Cars cars = new Cars(List.of(pobi, woni)); + + // when + List lines = OutputView.formatRoundResult(cars); + + // then + assertThat(lines).containsExactly("pobi : --", "woni : -"); + } + + @DisplayName("최종 우승자 문자열은 '최종 우승자 : pobi' 형식으로 생성되어야 한다") + @Test + void 단독_우승_문자열_포맷_테스트() { + // given + List winners = List.of("pobi"); + + // when + String line = OutputView.formatWinners(winners); + + // then + assertThat(line).isEqualTo("최종 우승자 : pobi"); + } + + @DisplayName("최종 우승자 문자열은 '최종 우승자 : pobi, jun' 형식으로 생성되어야 한다") + @Test + void 공동_우승_문자열_포맷_테스트() { + // given + List winners = List.of("pobi", "jun"); + + // when + String line = OutputView.formatWinners(winners); + + // then + assertThat(line).isEqualTo("최종 우승자 : pobi, jun"); + } +} \ No newline at end of file