diff --git a/README.md b/README.md new file mode 100644 index 00000000..7cad491c --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +### 1단계 +- 자동차는 이름을 갖는다. +- 자동차는 0-9 사이 랜덤 값을 구한 후 4이상이면 전진, 3이하면 멈춘다. +- 3항 연산자를 쓰지 않는다. +- else 예약어, switch/case 허용하지 않는다. +- 메소드 길이가 15라인이 넘어가지 않도록 구현한다 +- 메인 메소드를 만들지 않는다. +- 테스트 코드 작성한다. + +### 2단계 +- n대의 자동차가 참여가능하다. +- 주어진 횟수동안 자동차 경주 게임을 완료한 후 누가 우승했는지 알 수 있다. +- 우승자는 한 명 이상일 수 있다. +- 나머진 1단계와 유사하다. + +### 3단계 +- 자동차에 이름 부여가능하고, 전진하는 자동차 출력할 때 자동차 이름을 같이 출력한다. +- 자동차 이름은 , 기준으로 구분하며 이름은 5자 이하만 가능하다. +- 메인 메소드를 추가한다. + +### 4단계 +- 모든 로직에 단위 테스트를 구현한다. +- mvc 패턴으로 리팩터링한다. + +### 개인적 목표 +1. 주석이 없어도 읽기 좋은 코드를 짜보자. +2. 메서드가 한가지 기능만 갖도록 분리하다가 따로 관리하는게 좋을 것 같으면 클래스로 구현하자. +3. 단위 테스트 핵심 원칙을 따르자. +4. 개념에 머무르고 있었던 객체 지향적 개념을 활용하여 코드를 작성해보자. +5. 발생할 수 있는 예외 케이스를 고려하여 방어적인 코드를 작성하고, 이를 테스트 코드로 확인하자. + + diff --git a/build.gradle b/build.gradle index 239f9e78..1b83bdf0 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,7 @@ dependencies { testImplementation platform('org.assertj:assertj-bom:3.25.1') testImplementation('org.junit.jupiter:junit-jupiter') testImplementation('org.assertj:assertj-core') + developmentOnly('org.springframework.boot:spring-boot-devtools') } test { diff --git a/src/main/java/racinggame/Main.java b/src/main/java/racinggame/Main.java new file mode 100644 index 00000000..90474f98 --- /dev/null +++ b/src/main/java/racinggame/Main.java @@ -0,0 +1,9 @@ +package racinggame; + +import racinggame.controller.RacingGameController; + +public class Main { + public static void main(String[] args) { + new RacingGameController().run(); + } +} diff --git a/src/main/java/racinggame/controller/RacingGameController.java b/src/main/java/racinggame/controller/RacingGameController.java new file mode 100644 index 00000000..0cfbf028 --- /dev/null +++ b/src/main/java/racinggame/controller/RacingGameController.java @@ -0,0 +1,33 @@ +package racinggame.controller; + +import racinggame.model.RacingGame; +import racinggame.model.car.policy.numbergenerator.ZeroToNineRandomGenerator; +import racinggame.model.car.policy.decider.ThresholdBaseMoveDecider; +import racinggame.model.car.policy.evaluator.GreaterThanOrEqualThresholdEvaluator; +import racinggame.model.car.policy.strategy.MoveStrategy; +import racinggame.model.car.policy.strategy.OneStepMoveStrategy; +import racinggame.view.InputView; +import racinggame.view.OutputView; + +import java.util.List; + +public class RacingGameController { + public void run(){ + List names= InputView.readCarNames(); + int moveCount = InputView.readMoveCount(); + + MoveStrategy strategy = new OneStepMoveStrategy( + new ZeroToNineRandomGenerator(), + new ThresholdBaseMoveDecider(new GreaterThanOrEqualThresholdEvaluator(4))); + + RacingGame game = new RacingGame(names,strategy); + + for (int i = 0; i < moveCount; i++) { + game.moveOneTurn(); + OutputView.printResult(game.getCars()); + } + + List winners = game.getWinners(); + OutputView.printWinner(winners); + } +} diff --git a/src/main/java/racinggame/model/RacingGame.java b/src/main/java/racinggame/model/RacingGame.java new file mode 100644 index 00000000..5c6d6fe0 --- /dev/null +++ b/src/main/java/racinggame/model/RacingGame.java @@ -0,0 +1,46 @@ +package racinggame.model; +import racinggame.model.car.Car; +import racinggame.model.car.policy.strategy.MoveStrategy; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class RacingGame { + private final List cars; + + public RacingGame(List names,MoveStrategy moveStrategy) { + this.cars = createCars(names,moveStrategy); + } + + private List createCars(List names, MoveStrategy strategy) { + return names.stream() + .map(name -> new Car(name,strategy)) + .collect(Collectors.toList()); + } + + public void moveOneTurn() { + for (Car car : cars) { + car.move(); + } + } + + public List getCars() { + return cars; + } + + public List getWinners(){ + int maxDistance=findMaxDistance(); + + return cars.stream() + .filter(car->car.getMovedDistance()==maxDistance) + .map(Car::getName).collect(Collectors.toList()); + } + + private int findMaxDistance(){ + return cars.stream() + .mapToInt(Car::getMovedDistance) + .max() + .orElse(0); + } +} diff --git a/src/main/java/racinggame/model/WinnerFinder.java b/src/main/java/racinggame/model/WinnerFinder.java new file mode 100644 index 00000000..0638477c --- /dev/null +++ b/src/main/java/racinggame/model/WinnerFinder.java @@ -0,0 +1,38 @@ +package racinggame.model; + +import racinggame.model.car.Car; + +import java.util.List; +import java.util.stream.Collectors; + +public class WinnerFinder { + private final List cars; + private final int moveCount; + public WinnerFinder(List cars, int moveCount){ + this.cars = cars; + this.moveCount = moveCount; + } + + public void play(){ + for(int i=0;i getWinners(){ + int maxDistance=maxDistance(); + return cars.stream().filter(car->car.getMovedDistance()==maxDistance).map(Car::getName).collect(Collectors.toList()); + } + + private void moveAllCars( ){ + for (Car car : cars) { + car.move(); + } + } + private int maxDistance(){ + int max=0; + for (Car car : cars) { + max=Math.max(max,car.getMovedDistance()); + } + return max; + } +} diff --git a/src/main/java/racinggame/model/car/Car.java b/src/main/java/racinggame/model/car/Car.java new file mode 100644 index 00000000..1ca461cf --- /dev/null +++ b/src/main/java/racinggame/model/car/Car.java @@ -0,0 +1,35 @@ +package racinggame.model.car; + +import racinggame.model.car.policy.strategy.MoveStrategy; + +import java.util.Objects; + +public class Car { + private final String name; + private int movedDistance=0; + + private final MoveStrategy moveStrategy; + + public Car(String name,MoveStrategy moveStrategy) { + if(name==null){ + throw new IllegalArgumentException("이름은 null 일 수 없습니다."); + } + if(name.isBlank()){ + throw new IllegalArgumentException("이름은 공백일 수 없습니다."); + } + this.name = name; + this.moveStrategy=moveStrategy; + } + + public void move(){ + int distance=moveStrategy.addStepSize(); + movedDistance+=distance; + } + + public int getMovedDistance() { + return movedDistance; + } + public String getName() { + return name; + } +} diff --git a/src/main/java/racinggame/model/car/policy/decider/MoveDecider.java b/src/main/java/racinggame/model/car/policy/decider/MoveDecider.java new file mode 100644 index 00000000..77e2ab20 --- /dev/null +++ b/src/main/java/racinggame/model/car/policy/decider/MoveDecider.java @@ -0,0 +1,5 @@ +package racinggame.model.car.policy.decider; + +public interface MoveDecider { + boolean canMove(int randomNumber); +} diff --git a/src/main/java/racinggame/model/car/policy/decider/ThresholdBaseMoveDecider.java b/src/main/java/racinggame/model/car/policy/decider/ThresholdBaseMoveDecider.java new file mode 100644 index 00000000..1436575e --- /dev/null +++ b/src/main/java/racinggame/model/car/policy/decider/ThresholdBaseMoveDecider.java @@ -0,0 +1,16 @@ +package racinggame.model.car.policy.decider; + +import racinggame.model.car.policy.evaluator.ThresholdEvaluator; + +public class ThresholdBaseMoveDecider implements MoveDecider { + private final ThresholdEvaluator evaluator; + + public ThresholdBaseMoveDecider(ThresholdEvaluator evaluator) { + this.evaluator = evaluator; + } + + @Override + public boolean canMove(int randomNumber){ + return evaluator.isSatisfied(randomNumber); + } +} diff --git a/src/main/java/racinggame/model/car/policy/evaluator/GreaterThanOrEqualThresholdEvaluator.java b/src/main/java/racinggame/model/car/policy/evaluator/GreaterThanOrEqualThresholdEvaluator.java new file mode 100644 index 00000000..3e0e5509 --- /dev/null +++ b/src/main/java/racinggame/model/car/policy/evaluator/GreaterThanOrEqualThresholdEvaluator.java @@ -0,0 +1,13 @@ +package racinggame.model.car.policy.evaluator; + +public class GreaterThanOrEqualThresholdEvaluator implements ThresholdEvaluator { + private final int threshold; + + public GreaterThanOrEqualThresholdEvaluator(int threshold) { + this.threshold = threshold; + } + @Override + public boolean isSatisfied(int value){ + return value >= threshold; + } +} diff --git a/src/main/java/racinggame/model/car/policy/evaluator/ThresholdEvaluator.java b/src/main/java/racinggame/model/car/policy/evaluator/ThresholdEvaluator.java new file mode 100644 index 00000000..ac40b49d --- /dev/null +++ b/src/main/java/racinggame/model/car/policy/evaluator/ThresholdEvaluator.java @@ -0,0 +1,5 @@ +package racinggame.model.car.policy.evaluator; + +public interface ThresholdEvaluator { + boolean isSatisfied(int value); +} diff --git a/src/main/java/racinggame/model/car/policy/numbergenerator/RandomGenerator.java b/src/main/java/racinggame/model/car/policy/numbergenerator/RandomGenerator.java new file mode 100644 index 00000000..5c28be53 --- /dev/null +++ b/src/main/java/racinggame/model/car/policy/numbergenerator/RandomGenerator.java @@ -0,0 +1,5 @@ +package racinggame.model.car.policy.numbergenerator; + +public interface RandomGenerator { + int generate(); +} diff --git a/src/main/java/racinggame/model/car/policy/numbergenerator/ZeroToNineRandomGenerator.java b/src/main/java/racinggame/model/car/policy/numbergenerator/ZeroToNineRandomGenerator.java new file mode 100644 index 00000000..750a06d4 --- /dev/null +++ b/src/main/java/racinggame/model/car/policy/numbergenerator/ZeroToNineRandomGenerator.java @@ -0,0 +1,11 @@ +package racinggame.model.car.policy.numbergenerator; +import java.util.Random; + +public class ZeroToNineRandomGenerator implements RandomGenerator { + private final Random random=new Random(); + + @Override + public int generate() { + return random.nextInt(10); + } +} diff --git a/src/main/java/racinggame/model/car/policy/strategy/MoveStrategy.java b/src/main/java/racinggame/model/car/policy/strategy/MoveStrategy.java new file mode 100644 index 00000000..c1bfb5fa --- /dev/null +++ b/src/main/java/racinggame/model/car/policy/strategy/MoveStrategy.java @@ -0,0 +1,5 @@ +package racinggame.model.car.policy.strategy; + +public interface MoveStrategy { + int addStepSize(); +} diff --git a/src/main/java/racinggame/model/car/policy/strategy/OneStepMoveStrategy.java b/src/main/java/racinggame/model/car/policy/strategy/OneStepMoveStrategy.java new file mode 100644 index 00000000..64133cb2 --- /dev/null +++ b/src/main/java/racinggame/model/car/policy/strategy/OneStepMoveStrategy.java @@ -0,0 +1,24 @@ +package racinggame.model.car.policy.strategy; + +import racinggame.model.car.policy.numbergenerator.RandomGenerator; +import racinggame.model.car.policy.decider.MoveDecider; + +public class OneStepMoveStrategy implements MoveStrategy { + private static final int STEP_SIZE = 1; + + private final RandomGenerator randomGenerator; + private final MoveDecider moveDecider; + + public OneStepMoveStrategy(RandomGenerator randomGenerator, MoveDecider moveDecider) { + this.randomGenerator = randomGenerator; + this.moveDecider = moveDecider; + } + @Override + public int addStepSize(){ + int randomNumber=randomGenerator.generate(); + if(moveDecider.canMove(randomNumber)){ + return STEP_SIZE; + } + return 0; + } +} diff --git a/src/main/java/racinggame/view/InputView.java b/src/main/java/racinggame/view/InputView.java new file mode 100644 index 00000000..210f3301 --- /dev/null +++ b/src/main/java/racinggame/view/InputView.java @@ -0,0 +1,44 @@ +package racinggame.view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.Set; +import java.util.stream.Collectors; + +public class InputView { + private static final String DELIMITER = ","; + private static final String CAR_NAME_INPUT_PROMPT ="경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; + + public static List readCarNames() { + Scanner scanner = new Scanner(System.in); + System.out.println(CAR_NAME_INPUT_PROMPT); + + List names = Arrays.stream(scanner.nextLine().split(DELIMITER)) + .map(String::trim) + .toList(); + + if (names.stream().anyMatch(name -> name.length() > 5)) { + throw new IllegalArgumentException("이름은 5자 이하만 가능합니다."); + } + + Set unique = names.stream().collect(Collectors.toSet()); + if (unique.size() != names.size()) { + throw new IllegalArgumentException("자동차 이름은 모두 고유해야 합니다."); + } + + return names; + } + + public static int readMoveCount() { + Scanner scanner = new Scanner(System.in); + System.out.println("시도할 회수는 몇회인가요?"); + + int moveCount = scanner.nextInt(); + if (moveCount <= 0) { + throw new IllegalArgumentException("moveCount는 0보다 큰 값이여야 합니다."); + } + + return moveCount; + } +} diff --git a/src/main/java/racinggame/view/OutputView.java b/src/main/java/racinggame/view/OutputView.java new file mode 100644 index 00000000..1f0662bf --- /dev/null +++ b/src/main/java/racinggame/view/OutputView.java @@ -0,0 +1,20 @@ +package racinggame.view; + +import racinggame.model.car.Car; + +import java.util.List; + +public class OutputView { + public static void printResult(List cars) { + System.out.println("\n실행 결과"); + for (Car car : cars) { + System.out.println(car.getName() + " : " + "-".repeat(car.getMovedDistance())); + } + System.out.println(); + } + + public static void printWinner(List winners) { + System.out.println(String.join(", ", winners) + "가 최종 우승했습니다."); + } + +} diff --git a/src/test/java/CarTest.java b/src/test/java/CarTest.java new file mode 100644 index 00000000..4ea2164d --- /dev/null +++ b/src/test/java/CarTest.java @@ -0,0 +1,60 @@ +import racinggame.model.car.Car; +import racinggame.model.car.policy.numbergenerator.RandomGenerator; +import racinggame.model.car.policy.decider.MoveDecider; +import racinggame.model.car.policy.decider.ThresholdBaseMoveDecider; +import racinggame.model.car.policy.evaluator.GreaterThanOrEqualThresholdEvaluator; +import racinggame.model.car.policy.evaluator.ThresholdEvaluator; +import racinggame.model.car.policy.strategy.MoveStrategy; +import racinggame.model.car.policy.strategy.OneStepMoveStrategy; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class CarTest { + + @Test + @DisplayName("moveStrategy에 따라 누적 이동 거리가 쌓인다") + void moveStrategy_따라_누적되는지_확인한다 () { + // give + MoveStrategy alwaysOne = () -> 1; + Car car = new Car("car1", alwaysOne); + + // when + car.move(); + car.move(); + + // then + assertThat(car.getMovedDistance()).isEqualTo(2); + assertThat(car.getName()).isEqualTo("car2"); + } + + @DisplayName("이름을 null로 둔다면 에러를 던지는지 확인한다.") + @Test + void 이름을_null로_둘_수_없음(){ + // given + MoveStrategy dummystrategy=()->0; + + // when & then + assertThatThrownBy(()->new Car(null, dummystrategy)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("이름은 null 일 수 없습니다."); + } + + @DisplayName("이름을 공백으로 둔다면 에러를 던지는지 확인한다.") + @Test + void 이름을_공백으로_둘_수_없음(){ + // given + MoveStrategy dummystrategy=()->0; + + // when & then + assertThatThrownBy(()->new Car(" ", dummystrategy)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("이름은 공백일 수 없습니다."); + } + + +} diff --git a/src/test/java/GreaterThanOrEqualThresholdEvaluatorTest.java b/src/test/java/GreaterThanOrEqualThresholdEvaluatorTest.java new file mode 100644 index 00000000..304aeb2e --- /dev/null +++ b/src/test/java/GreaterThanOrEqualThresholdEvaluatorTest.java @@ -0,0 +1,37 @@ +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racinggame.model.car.policy.evaluator.GreaterThanOrEqualThresholdEvaluator; +import racinggame.model.car.policy.evaluator.ThresholdEvaluator; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class GreaterThanOrEqualThresholdEvaluatorTest { + @Test + @DisplayName("value가 threshold 이상이면 true") + void threshold_이상이면_크면_참() { + // given + ThresholdEvaluator evaluator = new GreaterThanOrEqualThresholdEvaluator(4); + + // when + boolean atThreshold=evaluator.isSatisfied(4); + boolean aboveThreshold=evaluator.isSatisfied(5); + + // then + assertThat(atThreshold).isTrue(); + assertThat(aboveThreshold).isTrue(); + } + + @Test + @DisplayName("value가 threshold 미만이면 false") + void threshold_미만이면_거짓() { + // given + ThresholdEvaluator evaluator = new GreaterThanOrEqualThresholdEvaluator(4); + + // when + boolean belowThreshold=evaluator.isSatisfied(2); + + // then + assertThat(belowThreshold).isFalse(); + + } +} diff --git a/src/test/java/InputViewTest.java b/src/test/java/InputViewTest.java new file mode 100644 index 00000000..c9fecb88 --- /dev/null +++ b/src/test/java/InputViewTest.java @@ -0,0 +1,54 @@ +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racinggame.view.InputView; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class InputViewTest { + private final InputStream systemIn = System.in; + + @AfterEach + void restore() { + System.setIn(systemIn); + } + @Test + @DisplayName("0 이하 입력 시 IllegalArgumentException 발생") + void invalidMoveCount(){ + // given + String testInput ="0\n"; + System.setIn(new ByteArrayInputStream(testInput.getBytes())); + + // when & then + assertThatThrownBy(()-> InputView.readMoveCount()) + .isInstanceOf(IllegalArgumentException.class).hasMessage("moveCount는 0보다 큰 값이여야 합니다."); + } + + @Test + @DisplayName("이름이 5자 초과일 경우 예외를 던진다") + void nameTooLong_throwsException() { + // given + System.setIn(new ByteArrayInputStream("123456\n".getBytes())); + + // when & then + assertThatThrownBy(InputView::readCarNames) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름은 5자 이하만 가능합니다."); + } + + @Test + @DisplayName("중복된 이름이 있을 경우 예외를 던진다") + void duplicateName_throwsException() { + // given + System.setIn(new ByteArrayInputStream("aa,bb,aa\n".getBytes())); + + // when & then + assertThatThrownBy(InputView::readCarNames) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("자동차 이름은 모두 고유해야 합니다."); + } + +} diff --git a/src/test/java/OneStepMoveStrategyTest.java b/src/test/java/OneStepMoveStrategyTest.java new file mode 100644 index 00000000..a87b0dfd --- /dev/null +++ b/src/test/java/OneStepMoveStrategyTest.java @@ -0,0 +1,35 @@ + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racinggame.model.car.policy.decider.MoveDecider; +import racinggame.model.car.policy.numbergenerator.RandomGenerator; +import racinggame.model.car.policy.strategy.OneStepMoveStrategy; + +import static org.assertj.core.api.Assertions.assertThat; + +class OneStepMoveStrategyTest { + + @Test + @DisplayName("Decider가 true일 때는 STEP_SIZE 만큼 반환") + void returnsStepSizeWhenDeciderTrue() { + // given + RandomGenerator fixedGenerator = () -> 8; + MoveDecider alwaysTrue = num -> true; + OneStepMoveStrategy strat = new OneStepMoveStrategy(fixedGenerator, alwaysTrue); + + // when & then + assertThat(strat.addStepSize()).isEqualTo(1); + } + + @Test + @DisplayName("Decider가 false일 때는 0 반환") + void returnsZeroWhenDeciderFalse() { + // given + RandomGenerator fixedGenerator = () -> 2; + MoveDecider alwaysFalse = num -> false; + OneStepMoveStrategy strategy = new OneStepMoveStrategy(fixedGenerator, alwaysFalse); + + // when & then + assertThat(strategy.addStepSize()).isZero(); + } +} diff --git a/src/test/java/RacingGameTest.java b/src/test/java/RacingGameTest.java new file mode 100644 index 00000000..c911377d --- /dev/null +++ b/src/test/java/RacingGameTest.java @@ -0,0 +1,24 @@ +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racinggame.model.RacingGame; +import racinggame.model.car.policy.strategy.MoveStrategy; + +import java.util.List; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +public class RacingGameTest { + @Test + @DisplayName("공동 우승자 발생한 경우 -> car1 car2") + void 공동_우승자_발생 () { + // given + MoveStrategy dummystrategy=()->1; + RacingGame game = new RacingGame(List.of("car1", "car2"), dummystrategy); + + // when + game.moveOneTurn(); + + // then + List winners = game.getWinners(); + assertThat(winners).containsExactlyInAnyOrder("car1", "car2"); + } +} diff --git a/src/test/java/ThresholdBaseMoveDeciderTest.java b/src/test/java/ThresholdBaseMoveDeciderTest.java new file mode 100644 index 00000000..ef2bd6b6 --- /dev/null +++ b/src/test/java/ThresholdBaseMoveDeciderTest.java @@ -0,0 +1,29 @@ +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racinggame.model.car.policy.decider.MoveDecider; +import racinggame.model.car.policy.decider.ThresholdBaseMoveDecider; +import racinggame.model.car.policy.evaluator.ThresholdEvaluator; + +import static org.assertj.core.api.Assertions.assertThat; + + +class ThresholdBaseMoveDeciderTest { + + @Test + @DisplayName("Evaluator 결과를 그대로 리턴한다") + void evaluator_결과_리턴 () { + // given + ThresholdEvaluator evaluator = new ThresholdEvaluator() { + @Override + public boolean isSatisfied(int value) { + return value == 7; + } + }; + MoveDecider decider = new ThresholdBaseMoveDecider(evaluator); + + // when & then + assertThat(decider.canMove(7)).isTrue(); + assertThat(decider.canMove(2)).isFalse(); + + } +} diff --git a/src/test/java/WinnerFinderTest.java b/src/test/java/WinnerFinderTest.java new file mode 100644 index 00000000..fa238077 --- /dev/null +++ b/src/test/java/WinnerFinderTest.java @@ -0,0 +1,61 @@ +import racinggame.model.car.Car; +import racinggame.model.car.policy.strategy.MoveStrategy; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racinggame.model.WinnerFinder; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class WinnerFinderTest { + + @Test + @DisplayName("우승자 1명일 경우 car1") + public void singleWinner(){ + // given + List cars = createCarsForTest(new int[]{3,2,1},new String[]{"car1","car2","car3"}); + WinnerFinder winner=new WinnerFinder(cars,1); + + // when + winner.play(); + + // then + List winners = winner.getWinners(); + assertThat(winners).containsExactly("car1"); + } + + @Test + @DisplayName("우승자 여러명일 경우 car1 car2") + public void multipleWinners(){ + // given + List cars = createCarsForTest(new int[]{3,3,1},new String[]{"car1","car2","car3"}); + WinnerFinder winner=new WinnerFinder(cars,1); + + // when + winner.play(); + + // then + List winners = winner.getWinners(); + assertThat(winners).containsExactly("car1", "car2"); + } + + + private List createCarsForTest(int[] distances,String[] names){ + List cars = new ArrayList<>(); + for (int i = 0; i < names.length; i++) { + cars.add(new Car(names[i],fixedStrategy(distances[i]))); + } + return cars; + } + + // 테스트 코드에만 존재하면 되므로 배포되지 않는 test 패키지안에 구현 + // MoveStrategy 은 addStepSize( ) 하나의 메소드만 있다. + // 함수형 인터페이스를 사용한다. + private MoveStrategy fixedStrategy(int distance){ + return ()->distance; + } + +} \ No newline at end of file