diff --git a/README.md b/README.md
index 491aece1..4f04776e 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,158 @@
-# java-racingcar-precourse
\ No newline at end of file
+# java-racingcar-precourse
+
+
+# 자동차 경주 - 프리코스
+
+## 프리코스 진행 방식
+
+--------
+
+## 진행 방식
+
+- 미션은 **과제 진행 요구사항, 기능 요구사항, 프로그래밍 요구사항** 세 가지로 구성되어 있다.
+- 세 개의 요구사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다.
+- 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.
+
+## 미션 제출 방법
+- 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다.
+ - GitHub을 활용한 제출 방법은 프리코스 진행 가이드 문서를 참고해 제출한다.
+ - 풀 리퀘스트 제목은 '[$이름] 프리코스 미션 제출합니다.'로 작성한다.
+- GitHub에 미션을 제출한 후 구글 폼에 PR 링크를 포함하여 최종 제출한다.
+
+## 과제 진행 소감
+과제를 수행하면서 느낀 점, 배운 점, 많은 시간을 투자한 부분 등 자유롭게 작성한다.
+
+**예시**
+
+```
+이번 미션은 생각만큼 쉽지 않았습니다.
+특히 기능을 분리해서 기능 단위로 커밋하는 것이 쉽지 않다는 것을 깨달았습니다.
+기능 단위로 분리하고 커밋하기 위해 다양한 방법으로 연습하였습니다.
+하지만 일단 기능을 분리해서 구현하고 나니 좀 더 명확하게 구현할 수 있다는 것을 느낄 수 있었습니다.
+미션을 수행하면서 더욱 성장한 걸 느꼈어요. :)
+```
+
+## 과제 제출 전 체크 리스트
+- 터미널에서 `java -version` 을 실행하여 Java 버전이 17인지 확인한다. Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 17로 실행되는지 확인한다.
+- 터미널에서 Mac 또는 Linux 사용자의 경우 `./gradlew clean test` 명령을 실행하고, Windows 사용자의 경우 `gradlew.bat clean test` 또
+는 `./gradlew.bat clean test` 명령을 실행할 때 모든 테스트가 아래와 같이 통과하는지 확인한다.
+
+ `BUILD SUCCESSFUL in 0s`
+
+## 자동차 경주
+
+----------
+## 과제 진행 요구 사항
+- 미션은 자동차 경주 저장소를 포크하고 클론하는 것으로 시작한다.
+- 기능을 구현하기 전 에 구현할 기능 목록을 정리해 추가한다. README.md
+- Git의 커밋 단위는 앞 단계에서 에 정리한 기능 목록 단위로 추가한다. README.md
+ - AngularJS Git Commit Message Conventions을 참고해 커밋 메시지를 작성한다.
+- 자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다.
+
+## 기능 요구사항
+
+초간단 자동차 경주 게임을 구현한다.
+
+- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
+- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
+- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
+- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
+- 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.
+- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
+- 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.
+- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.
+- `Exception`이 아닌 `IllegalArgumentException`, `IllegalStateException` 등과 같은 명확한 유형을 처리한다.
+
+
+
+## 실행 결과
+
+```
+경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
+pobi,woni,jun
+시도할 회수는 몇회인가요?
+5
+
+실행 결과
+pobi : -
+woni :
+jun : -
+
+pobi : --
+woni : -
+jun : --
+
+pobi : ---
+woni : --
+jun : ---
+
+pobi : ----
+woni : ---
+jun : ----
+
+pobi : -----
+woni : ----
+jun : -----
+
+최종 우승자 : pobi, jun
+```
+
+## 프로그래밍 요구 사항 1
+
+- JDK 17 버전에서 실행 가능해야 한다.
+- 프로그램을 실행하는 시작점은 `Application`의 `main()`이다.
+- build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.
+- 프로그램 종료 시 를 호출하지 않는다. `System.exit()`
+- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.
+
+## 프로그래밍 요구 사항 2
+- 자바 코드 컨벤션을 지키면서 프로그래밍한다.
+ - 기본적으로 Google Java Style Guide를 원칙으로 한다.
+ - 단, 들여쓰기는 '2 spaces'가 아닌 '4 spaces'로 한다.
+- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
+ - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
+ - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
+- 3항 연산자를 쓰지 않는다.
+- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
+- JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다.
+ - 테스트 도구 사용법이 익숙하지 않다면 아래 문서를 참고하여 학습한 후 테스트를 구현한다.
+ - JUnit 5 User Guide
+ - AssertJ User Guide
+ - AssertJ Exception Assertions
+ - Guide to JUnit 5 Parameterized Tests
+
+## 프로그래밍 요구 사항 3
+- 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다.
+ - 함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다.
+- else 예약어를 쓰지 않는다.
+ - else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
+ - 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
+- 도메인 로직에 단위 테스트를 구현해야 한다. 단, UI(System.out, System.in, Scanner) 로직은 제외한다.
+ - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다.
+ - 힌트: MVC 패턴 기반으로 구현한 후, View와 Controller를 제외한 Model에 대한 단위 테스트 추가에 집중한다
+
+## 기능 구현 목록
+1. 게임 초기화
+ - [x] 자동차 이름 입력
+ - [x] `예외 처리` : 빈 값 입력
+ - [x] `예외 처리` : 이름이 5자 초과
+ - [ ] `예외 처리` : 구분자 예외 처리
+ - [x] 이동 횟수 입력
+ - [x] `예외 처리` : 빈 값 입력
+ - [x] `예외 처리` : 음수 입력
+ - [x] `예외 처리` : 숫자가 아닌 입력
+
+2. 게임 진행
+ - [x] 입력된 횟수만큼 이동
+ - [x] 0 ~ 9 까지의 무작위 숫자 뽑기
+ - [x] 4 이상 일 시 전진
+ - [x] 3 이하인 경우 동작 하지 않음
+ - [x] 실행 된 결과 출력
+ - [x] 맨 처음 출력 시 `실행 결과`를 출력한다.
+ - [x] `${이름}: ---` 와 같은 형식으로 출력한다.
+ - [x] 이동 문자는 `-`로 한다.
+ - [x] 이름이 입력 된 순서대로 실행 결과를 출력한다.
+3. 게임 종료
+ - [x] 우승자 출력
+ - [x] 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.
+ - [x] `최종 우승자 : ${이름}` 형태로 출력한다.
\ No newline at end of file
diff --git a/src/main/java/main/java/racingcar/Application.java b/src/main/java/main/java/racingcar/Application.java
new file mode 100644
index 00000000..7164a229
--- /dev/null
+++ b/src/main/java/main/java/racingcar/Application.java
@@ -0,0 +1,8 @@
+package main.java.racingcar;
+
+public class Application {
+ public static void main(String[] args) {
+ MainController mainController = new MainController();
+ mainController.run();
+ }
+}
diff --git a/src/main/java/main/java/racingcar/MainController.java b/src/main/java/main/java/racingcar/MainController.java
new file mode 100644
index 00000000..2f6dfbb8
--- /dev/null
+++ b/src/main/java/main/java/racingcar/MainController.java
@@ -0,0 +1,45 @@
+package main.java.racingcar;
+
+import main.java.racingcar.domain.Car;
+import main.java.racingcar.domain.Cars;
+import main.java.racingcar.domain.TrialCount;
+import main.java.racingcar.message.ViewMessage;
+import main.java.racingcar.view.InputView;
+import main.java.racingcar.view.OutputView;
+
+import java.util.List;
+
+public class MainController {
+
+ public void run() {
+ Cars cars = this.initCars();
+ TrialCount trialCount = this.initTrialCount();
+ this.proceedGame(cars, trialCount);
+ this.endGame(cars);
+ }
+
+ private Cars initCars() {
+ String inputValue = InputView.inputString(ViewMessage.INPUT_CAR_NAME);
+ return Cars.createByString(inputValue);
+ }
+
+ private TrialCount initTrialCount() {
+ int count = InputView.inputInteger(ViewMessage.INPUT_TRIAL_COUNT);
+ return new TrialCount(count);
+ }
+
+ private void proceedGame(Cars cars, TrialCount trialCount) {
+ int moveCount = 0;
+ OutputView.printMessage(ViewMessage.GAME_RESULT);
+ while(trialCount.isMoreThan(moveCount)) {
+ cars.commandAllCarsToMove();
+ OutputView.printMovingResult(cars);
+ moveCount++;
+ }
+ }
+
+ private void endGame(Cars cars) {
+ List winner = cars.getWinnerList();
+ OutputView.printWinner(winner);
+ }
+}
diff --git a/src/main/java/main/java/racingcar/Utils.java b/src/main/java/main/java/racingcar/Utils.java
new file mode 100644
index 00000000..8017ef76
--- /dev/null
+++ b/src/main/java/main/java/racingcar/Utils.java
@@ -0,0 +1,17 @@
+package main.java.racingcar;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+public class Utils {
+ public static List splitByDelimiter(String str, String delimiter) {
+ return Arrays.asList(str.split(delimiter));
+ }
+
+ public static boolean isFourOrMore() {
+ Random random = new Random();
+ int randomNumber = random.nextInt(10); // 0에서 9 사이의 난수 생성
+ return randomNumber >= 4;
+ }
+}
diff --git a/src/main/java/main/java/racingcar/domain/Car.java b/src/main/java/main/java/racingcar/domain/Car.java
new file mode 100644
index 00000000..1dff36ad
--- /dev/null
+++ b/src/main/java/main/java/racingcar/domain/Car.java
@@ -0,0 +1,38 @@
+package main.java.racingcar.domain;
+
+public class Car {
+
+ public static final String CAR_POSITION_SIGN = "-";
+ public static final int START_POSITION = 0;
+ private final CarName name;
+ private final int speed;
+ private final Position position;
+ public Car(CarName carName) {
+ this.name = carName;
+ this.speed = 1;
+ this.position = new Position(START_POSITION);
+ }
+
+ public void moveForwardIfTrue(boolean isForward) {
+ if(isForward) {
+ this.position.increment(speed);
+ }
+ }
+
+ public boolean isSamePosition(int position) {
+ return this.position.getAmount() == position;
+ }
+
+ public String getName() {
+ return this.name.getName();
+ }
+
+ public int getPosition() {
+ return this.position.getAmount();
+ }
+
+ @Override
+ public String toString() {
+ return this.name.getName() + " : " + this.CAR_POSITION_SIGN.repeat(this.position.getAmount());
+ }
+}
diff --git a/src/main/java/main/java/racingcar/domain/CarName.java b/src/main/java/main/java/racingcar/domain/CarName.java
new file mode 100644
index 00000000..2d8943a5
--- /dev/null
+++ b/src/main/java/main/java/racingcar/domain/CarName.java
@@ -0,0 +1,34 @@
+package main.java.racingcar.domain;
+
+import main.java.racingcar.message.ErrorMessage;
+
+public class CarName {
+
+ private static final int MAX_NAME_LENGTH = 5;
+ private final String name;
+ public CarName(String name) {
+ this.validateName(name);
+ this.name = name;
+ }
+
+ private void validateName(String name) {
+ this.validateBlank(name);
+ this.validateNameLength(name);
+ }
+
+ private void validateBlank(String name) {
+ if (name == null || name.isBlank()) {
+ throw new IllegalArgumentException(ErrorMessage.INVALID_CAR_NAME);
+ }
+ }
+
+ private void validateNameLength(String name) {
+ if (name.length() > MAX_NAME_LENGTH) {
+ throw new IllegalArgumentException(ErrorMessage.INVALID_CAR_NAME);
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/main/java/racingcar/domain/Cars.java b/src/main/java/main/java/racingcar/domain/Cars.java
new file mode 100644
index 00000000..f7bd4ec8
--- /dev/null
+++ b/src/main/java/main/java/racingcar/domain/Cars.java
@@ -0,0 +1,45 @@
+package main.java.racingcar.domain;
+
+import main.java.racingcar.Utils;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public record Cars(List carList) {
+
+ public static final String CAR_NAME_DELIMITER = ",";
+
+ public static Cars createByString(String carsString) {
+ List carList = Utils.splitByDelimiter(carsString, CAR_NAME_DELIMITER)
+ .stream()
+ .map(CarName::new)
+ .map(Car::new)
+ .toList();
+ return new Cars(carList);
+ }
+
+ public void commandAllCarsToMove() {
+ carList.forEach(car ->
+ car.moveForwardIfTrue(Utils.isFourOrMore()));
+ }
+
+ public List getWinnerList() {
+ Integer winnerPosition = getWinnerPosition();
+ return carList.stream()
+ .filter(car -> car.isSamePosition(winnerPosition))
+ .collect(Collectors.toList());
+ }
+
+ private Integer getWinnerPosition() {
+ return carList.stream()
+ .map(Car::getPosition)
+ .max(Integer::compareTo)
+ .orElse(0);
+ }
+
+ @Override
+ public String toString() {
+ return carList.stream()
+ .map(Car::toString)
+ .collect(Collectors.joining("\n"));
+ }
+}
diff --git a/src/main/java/main/java/racingcar/domain/Position.java b/src/main/java/main/java/racingcar/domain/Position.java
new file mode 100644
index 00000000..eafc3665
--- /dev/null
+++ b/src/main/java/main/java/racingcar/domain/Position.java
@@ -0,0 +1,27 @@
+package main.java.racingcar.domain;
+
+import main.java.racingcar.message.ErrorMessage;
+
+public class Position {
+
+ private int amount;
+
+ public Position(int amount){
+ this.validate(amount);
+ this.amount = amount;
+ }
+
+ public void validate(int amount) {
+ if (amount < 0) {
+ throw new IllegalArgumentException(ErrorMessage.INVALID_NEGATIVE);
+ }
+ }
+
+ public void increment(int speed) {
+ this.amount += speed;
+ }
+
+ public int getAmount() {
+ return amount;
+ }
+}
diff --git a/src/main/java/main/java/racingcar/domain/TrialCount.java b/src/main/java/main/java/racingcar/domain/TrialCount.java
new file mode 100644
index 00000000..a72c200b
--- /dev/null
+++ b/src/main/java/main/java/racingcar/domain/TrialCount.java
@@ -0,0 +1,18 @@
+package main.java.racingcar.domain;
+
+import main.java.racingcar.message.ErrorMessage;
+
+public record TrialCount(int count) {
+ public TrialCount {
+ this.validate(count);
+ }
+ private void validate(int count) {
+ if (count < 0) {
+ throw new IllegalArgumentException(ErrorMessage.INVALID_NEGATIVE);
+ }
+ }
+
+ public boolean isMoreThan(int count) {
+ return this.count > count;
+ }
+}
diff --git a/src/main/java/main/java/racingcar/message/ErrorMessage.java b/src/main/java/main/java/racingcar/message/ErrorMessage.java
new file mode 100644
index 00000000..6b98372f
--- /dev/null
+++ b/src/main/java/main/java/racingcar/message/ErrorMessage.java
@@ -0,0 +1,13 @@
+package main.java.racingcar.message;
+
+public class ErrorMessage {
+
+ private ErrorMessage() {
+ }
+
+ public static String INPUT_BLANK = "[ERROR] 빈 값을 입력 했습니다.";
+ public static String INVALID_CAR_NAME = "[ERROR] 자동차 이름은 5자 이하여야 합니다.";
+
+ public static String INPUT_NOT_INTEGER = "[ERROR] 숫자를 입력해야 합니다";
+ public static String INVALID_NEGATIVE = "0보다 작은 값을 입력할 수 없습니다.";
+}
diff --git a/src/main/java/main/java/racingcar/message/ViewMessage.java b/src/main/java/main/java/racingcar/message/ViewMessage.java
new file mode 100644
index 00000000..71893ef8
--- /dev/null
+++ b/src/main/java/main/java/racingcar/message/ViewMessage.java
@@ -0,0 +1,14 @@
+package main.java.racingcar.message;
+
+public class ViewMessage {
+
+ private ViewMessage() {
+ }
+ public static String INPUT_CAR_NAME = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)";
+ public static String INPUT_TRIAL_COUNT = "시도할 회수는 몇회인가요?";
+ public static String GAME_RESULT = "\n실행 결과";
+ public static String WINNER_RESULT_FORMAT = "최종 우승자 : %s";
+
+ public static String WINNER_RESULT_DELIMITER = ", ";
+
+}
diff --git a/src/main/java/main/java/racingcar/view/InputView.java b/src/main/java/main/java/racingcar/view/InputView.java
new file mode 100644
index 00000000..da5cd4f7
--- /dev/null
+++ b/src/main/java/main/java/racingcar/view/InputView.java
@@ -0,0 +1,38 @@
+package main.java.racingcar.view;
+
+
+import main.java.racingcar.message.ErrorMessage;
+
+import java.util.Scanner;
+
+public class InputView {
+ private static final Scanner scanner = new Scanner(System.in);
+ public static String inputString(String message) {
+ System.out.println(message);
+ String inputValue = scanner.nextLine();
+ validateBlank(inputValue);
+ return inputValue;
+ }
+
+ public static void validateBlank(String input) {
+ if (input.isBlank()) {
+ throw new IllegalArgumentException(ErrorMessage.INPUT_BLANK);
+ }
+ }
+
+ public static int inputInteger(String message) {
+ System.out.println(message);
+ String inputValue = scanner.nextLine();
+ validateBlank(inputValue);
+ validateInteger(inputValue);
+ return Integer.parseInt(inputValue);
+ }
+
+ public static void validateInteger(String input) {
+ try {
+ Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(ErrorMessage.INPUT_NOT_INTEGER);
+ }
+ }
+}
diff --git a/src/main/java/main/java/racingcar/view/OutputView.java b/src/main/java/main/java/racingcar/view/OutputView.java
new file mode 100644
index 00000000..a7181956
--- /dev/null
+++ b/src/main/java/main/java/racingcar/view/OutputView.java
@@ -0,0 +1,29 @@
+package main.java.racingcar.view;
+
+import main.java.racingcar.domain.Car;
+import main.java.racingcar.domain.Cars;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static main.java.racingcar.message.ViewMessage.WINNER_RESULT_DELIMITER;
+import static main.java.racingcar.message.ViewMessage.WINNER_RESULT_FORMAT;
+
+public class OutputView {
+
+ public static void printMessage(String message) {
+ System.out.println(message);
+ }
+
+ public static void printMovingResult(Cars cars) {
+ System.out.println(cars);
+ System.out.println();
+ }
+
+ public static void printWinner(List winner) {
+ String winnerStr = winner.stream()
+ .map(Car::getName)
+ .collect(Collectors.joining(WINNER_RESULT_DELIMITER));
+ System.out.printf(WINNER_RESULT_FORMAT, winnerStr);
+ }
+}
diff --git a/src/test/java/racingcar/CarTest.java b/src/test/java/racingcar/CarTest.java
new file mode 100644
index 00000000..fb43406c
--- /dev/null
+++ b/src/test/java/racingcar/CarTest.java
@@ -0,0 +1,33 @@
+package racingcar;
+
+import main.java.racingcar.domain.Car;
+import main.java.racingcar.domain.CarName;
+import main.java.racingcar.message.ErrorMessage;
+import org.assertj.core.api.Assertions;
+import org.assertj.core.api.Assertions.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CarTest {
+
+ @DisplayName("자동차 이름 생성 성공 테스트")
+ @ParameterizedTest
+ @ValueSource(strings = {"a", "jin", "a2", "abcd", "12345"})
+ void CarNameSuccessTest(String name) {
+ Car car = new Car(new CarName(name));
+ assertThat(car.getName()).isEqualTo(name);
+ }
+
+ @DisplayName("자동차 이름 생성 실패 테스트")
+ @ParameterizedTest
+ @ValueSource(strings = {"", " ", "5자를넘는이름테스트", "123456"})
+ void CarNameFailTest(String name) {
+ Assertions.assertThatThrownBy(() ->{
+ new Car(new CarName(name));
+ }).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(ErrorMessage.INVALID_CAR_NAME);
+ }
+}
diff --git a/src/test/java/racingcar/CarsTest.java b/src/test/java/racingcar/CarsTest.java
new file mode 100644
index 00000000..0a323fe6
--- /dev/null
+++ b/src/test/java/racingcar/CarsTest.java
@@ -0,0 +1,55 @@
+package racingcar;
+
+import main.java.racingcar.domain.Car;
+import main.java.racingcar.domain.CarName;
+import main.java.racingcar.domain.Cars;
+import main.java.racingcar.message.ErrorMessage;
+import org.assertj.core.api.Assertions;
+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("createByString 성공 테스트")
+ @Test
+ void createByStringSuccessTest() {
+ Cars cars = Cars.createByString("A,B,C,D");
+ Cars answer = new Cars(List.of(
+ new Car(new CarName("A")),
+ new Car(new CarName("B")),
+ new Car(new CarName("C")),
+ new Car(new CarName("D"))
+ ));
+ assertThat(cars.toString()).isEqualTo(answer.toString());
+ }
+
+ @DisplayName("createByString 실패 테스트")
+ @Test
+ void createByStringFailTest() {
+ Assertions.assertThatThrownBy(() -> Cars.createByString("A B C D"))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(ErrorMessage.INVALID_CAR_NAME);
+ }
+
+ @DisplayName("commandAllCarsToMove를 실행하면 최개 실행 횟수까지 전진한다.")
+ @Test
+ void commandAllCarsToMove() {
+ Car car1 = new Car(new CarName("car1"));
+ Car car2 = new Car(new CarName("car2"));
+ Car car3 = new Car(new CarName("car3"));
+
+ Cars cars = new Cars(List.of(car1, car2, car3));
+
+ cars.commandAllCarsToMove();
+ cars.commandAllCarsToMove();
+ cars.commandAllCarsToMove();
+
+ assertThat(car1.getPosition()).isLessThanOrEqualTo(3);
+ assertThat(car1.getPosition()).isLessThanOrEqualTo(3);
+ assertThat(car1.getPosition()).isLessThanOrEqualTo(3);
+ }
+}
diff --git a/src/test/java/racingcar/ConverterTest.java b/src/test/java/racingcar/ConverterTest.java
new file mode 100644
index 00000000..d6f515c8
--- /dev/null
+++ b/src/test/java/racingcar/ConverterTest.java
@@ -0,0 +1,29 @@
+package racingcar;
+
+import main.java.racingcar.Utils;
+import org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ConverterTest {
+ @ParameterizedTest
+ @MethodSource("splitByDelimiterArguments")
+ void splitByDelimiterTest(String src, String delimiter, List expected) {
+ assertThat(Utils.splitByDelimiter(src, delimiter)).isEqualTo(expected);
+ }
+
+ static Stream splitByDelimiterArguments() {
+ return Stream.of(
+ Arguments.of("a,b,c", ",", List.of("a", "b", "c")),
+ Arguments.of("a b c", " ", List.of("a", "b", "c")),
+ Arguments.of("a,b c", " ", List.of("a,b", "c")),
+ Arguments.of(",,,", ",", List.of())
+ );
+ }
+}
diff --git a/src/test/java/racingcar/MovingTest.java b/src/test/java/racingcar/MovingTest.java
new file mode 100644
index 00000000..fe6ad7a7
--- /dev/null
+++ b/src/test/java/racingcar/MovingTest.java
@@ -0,0 +1,36 @@
+package racingcar;
+
+import main.java.racingcar.domain.Car;
+import main.java.racingcar.domain.CarName;
+import org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MovingTest {
+
+ @DisplayName("움직이지 않으면 위치는 0이다.")
+ @Test
+ void moveFailTest() {
+ Car car = new Car(new CarName("car"));
+ assertThat(car.isSamePosition(0)).isTrue();
+ }
+
+ @DisplayName("자동차가 1회 움직이면 위치는 1이다.")
+ @Test
+ void moveSuccessTest() {
+ Car car = new Car(new CarName("car"));
+ car.moveForwardIfTrue(true);
+ assertThat(car.isSamePosition(1)).isTrue();
+ }
+
+ @DisplayName("자동차와 위치가 다른 경우 isSamePosition은 false이다.")
+ @Test
+ void moveSamePositionFailTest() {
+ Car car = new Car(new CarName("car"));
+ car.moveForwardIfTrue(true);
+ car.moveForwardIfTrue(true);
+ assertThat(car.isSamePosition(1)).isFalse();
+ }
+}
diff --git a/src/test/java/racingcar/TrialCountTest.java b/src/test/java/racingcar/TrialCountTest.java
new file mode 100644
index 00000000..eaee0f72
--- /dev/null
+++ b/src/test/java/racingcar/TrialCountTest.java
@@ -0,0 +1,38 @@
+package racingcar;
+
+import main.java.racingcar.domain.TrialCount;
+import main.java.racingcar.message.ErrorMessage;
+import org.assertj.core.api.Assertions;
+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;
+
+public class TrialCountTest {
+
+ @DisplayName("TrialCount 0으로 생성시 IllegalArgumentException 예외를 발생시킨다.")
+ @ParameterizedTest
+ @ValueSource(ints = {0, 1, 2, 3, 4, 5, 9999999})
+ void trialCountSuccess(int count) {
+ Assertions.assertThatNoException()
+ .isThrownBy(() -> new TrialCount(count));
+ }
+
+ @DisplayName("TrialCount 음수로 생성시 IllegalArgumentException 예외를 발생시킨다.")
+ @ParameterizedTest
+ @ValueSource(ints = {-1, -2, -3, -9999})
+ void trialCountException(int count) {
+ Assertions.assertThatThrownBy(() -> new TrialCount(count))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(ErrorMessage.INVALID_NEGATIVE);
+ }
+
+ @DisplayName("isMoreThan.")
+ @Test
+ void moreThan() {
+ TrialCount trialCount = new TrialCount(10);
+ Assertions.assertThat(trialCount.isMoreThan(9)).isTrue();
+ Assertions.assertThat(trialCount.isMoreThan(10)).isFalse();
+ Assertions.assertThat(trialCount.isMoreThan(11)).isFalse();
+ }
+}
diff --git a/src/test/java/racingcar/WinnerTest.java b/src/test/java/racingcar/WinnerTest.java
new file mode 100644
index 00000000..3874d372
--- /dev/null
+++ b/src/test/java/racingcar/WinnerTest.java
@@ -0,0 +1,67 @@
+package racingcar;
+
+import main.java.racingcar.domain.Car;
+import main.java.racingcar.domain.CarName;
+import main.java.racingcar.domain.Cars;
+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 WinnerTest {
+ @DisplayName("우승자가 1명만 있는 경우 리스트에는 한 명만 있다.")
+ @Test
+ void getSingleWinnerTest() {
+ Car car1 = new Car(new CarName("car1"));
+ Car car2 = new Car(new CarName("car2"));
+ Car car3 = new Car(new CarName("car3"));
+
+ Cars cars = new Cars(List.of(car1, car2, car3));
+
+ car1.moveForwardIfTrue(true);
+ car2.moveForwardIfTrue(false);
+ car3.moveForwardIfTrue(false);
+
+ List winner = cars.getWinnerList();
+
+ assertThat(winner).containsExactly(car1);
+ }
+
+ @DisplayName("우승자가 1명만 있는 경우 리스트에는 한 명만 있다.")
+ @Test
+ void getDoubleWinnerTest() {
+ Car car1 = new Car(new CarName("car1"));
+ Car car2 = new Car(new CarName("car2"));
+ Car car3 = new Car(new CarName("car3"));
+
+ Cars cars = new Cars(List.of(car1, car2, car3));
+
+ car1.moveForwardIfTrue(true);
+ car2.moveForwardIfTrue(true);
+ car3.moveForwardIfTrue(false);
+
+ List winner = cars.getWinnerList();
+
+ assertThat(winner).containsExactly(car1, car2);
+ }
+
+ @DisplayName("우승자가 1명만 있는 경우 리스트에는 한 명만 있다.")
+ @Test
+ void getTripleWinnerTest() {
+ Car car1 = new Car(new CarName("car1"));
+ Car car2 = new Car(new CarName("car2"));
+ Car car3 = new Car(new CarName("car3"));
+
+ Cars cars = new Cars(List.of(car1, car2, car3));
+
+ car1.moveForwardIfTrue(true);
+ car2.moveForwardIfTrue(true);
+ car3.moveForwardIfTrue(true);
+
+ List winner = cars.getWinnerList();
+
+ assertThat(winner).containsExactly(car1, car2, car3);
+ }
+}