From 8217daab18315786c74638170636f84e227748f3 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sun, 16 Apr 2023 11:19:52 +0900 Subject: [PATCH 01/36] =?UTF-8?q?refactor:=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=20=EB=8B=A8=EC=9C=84=ED=85=8C=EC=8A=A4=ED=8A=B8=20Mockito=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../racingcar/service/RacingGameServiceTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 6ea963f1f..866a806ba 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -7,6 +7,9 @@ import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import racingcar.dao.CarDao; +import racingcar.dao.GameDao; import racingcar.domain.NumberGenerator; import racingcar.dto.GameRequest; import racingcar.dto.GameResponse; @@ -19,13 +22,10 @@ class RacingGameServiceTest { @Test void 자동차_경주를_진행한다() { // given + final GameDao gameDao = Mockito.mock(GameDao.class); + final CarDao carDao = Mockito.mock(CarDao.class); final NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 3)); - final RacingGameService racingGameService = new RacingGameService( - numberGenerator, - (trialCount, winners) -> 1, - (gameId, cars) -> { - } - ); + final RacingGameService racingGameService = new RacingGameService(numberGenerator, gameDao, carDao); final GameRequest gameRequest = new GameRequest("브리,비버,허브", 1); // when From dc8a39090d1d902485eadbc26d1bc304aa11b070 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 08:05:01 +0900 Subject: [PATCH 02/36] =?UTF-8?q?refactor:=20NumberGenerator=20Bean=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=ED=8C=8C=EC=9D=BC=EC=9D=84=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20=EB=93=B1=EB=A1=9D=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/config/RacingGameConfig.java | 15 +++++++++++++++ .../racingcar/domain/RandomNumberGenerator.java | 2 -- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 src/main/java/racingcar/config/RacingGameConfig.java diff --git a/src/main/java/racingcar/config/RacingGameConfig.java b/src/main/java/racingcar/config/RacingGameConfig.java new file mode 100644 index 000000000..c4c9b540b --- /dev/null +++ b/src/main/java/racingcar/config/RacingGameConfig.java @@ -0,0 +1,15 @@ +package racingcar.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import racingcar.domain.NumberGenerator; +import racingcar.domain.RandomNumberGenerator; + +@Configuration +public class RacingGameConfig { + + @Bean + public NumberGenerator numberGenerator() { + return new RandomNumberGenerator(); + } +} diff --git a/src/main/java/racingcar/domain/RandomNumberGenerator.java b/src/main/java/racingcar/domain/RandomNumberGenerator.java index d7369772c..87a5103c0 100644 --- a/src/main/java/racingcar/domain/RandomNumberGenerator.java +++ b/src/main/java/racingcar/domain/RandomNumberGenerator.java @@ -1,9 +1,7 @@ package racingcar.domain; import java.util.Random; -import org.springframework.stereotype.Component; -@Component public class RandomNumberGenerator implements NumberGenerator { private static final int NUMBER_UPPER_BOUND = 10; From 3f180ae503ac034f1aedabf8900c376d245a32a4 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 08:42:54 +0900 Subject: [PATCH 03/36] =?UTF-8?q?test:=20WebRacingGameController=20?= =?UTF-8?q?=EC=8A=AC=EB=9D=BC=EC=9D=B4=EC=8A=A4=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WebRacingGameControllerTest.java | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java index ca6c5da15..559bcb0c2 100644 --- a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java @@ -2,24 +2,29 @@ import static org.hamcrest.Matchers.hasSize; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.any; +import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; +import racingcar.dto.CarDto; import racingcar.dto.GameRequest; +import racingcar.dto.GameResponse; +import racingcar.service.RacingGameService; -@SpringBootTest -@AutoConfigureMockMvc +@WebMvcTest @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") class WebRacingGameControllerTest { @@ -30,19 +35,30 @@ class WebRacingGameControllerTest { @Autowired private ObjectMapper objectMapper; + @MockBean + private RacingGameService racingGameService; + @Test - void 게임_진행() throws Exception { + void 게임을_진행한다() throws Exception { // given final GameRequest gameRequest = new GameRequest("비버,허브", 1); final String request = objectMapper.writeValueAsString(gameRequest); + final GameResponse gameResponse = new GameResponse( + "비버", + List.of(new CarDto("비버", 1), new CarDto("허브", 0)) + ); + given(racingGameService.play(any(GameRequest.class))) + .willReturn(gameResponse); // expect mockMvc.perform(post("/plays") .content(request) - .contentType(MediaType.APPLICATION_JSON)) + .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) - .andExpect(jsonPath("winners").exists()) + .andExpect(jsonPath("winners").value("비버")) .andExpect(jsonPath("racingCars", hasSize(2))) + .andExpect(jsonPath("$.racingCars[0].name").value("비버")) + .andExpect(jsonPath("$.racingCars[0].position").value(1)) .andDo(print()); } } From 5a5bb8cc72a81a87ac0877a4a83feb624e54f5d4 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 09:27:18 +0900 Subject: [PATCH 04/36] =?UTF-8?q?docs:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 ++++++++++++++++- http-request.http | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 http-request.http diff --git a/README.md b/README.md index 755a2974b..dab6af903 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,23 @@ ### 요구사항 - [x] 자동차 경주 코드 가져오기 -- [x] 웹 요청/응답 구현하기 +- [x] 게임 실행 기능 - [x] 게임 실행 요청 구현 - [x] 게임 저장 기능 구현 - [x] 자동차 저장 기능 구현 +- [ ] 게임 조회 기능 + - [ ] 게임 조회 요청 구현 +- [ ] 콘솔 애플리케이션 출력 변경 + - [ ] 중간 과정을 출력하는 로직 제거 + - [ ] 우승자와 플레이어별 최종 이동거리를 출력하도록 수정 +- [ ] 콘솔과 웹의 중복 코드 제거 + - [ ] 중복 로직 제거 - [x] DB 연동하기 + +### Database + +image + +### API + +[API 명세](http-request.http) diff --git a/http-request.http b/http-request.http new file mode 100644 index 000000000..93c9bea86 --- /dev/null +++ b/http-request.http @@ -0,0 +1,14 @@ +### 게임 실행 + +POST localhost:8080/plays +Content-Type: application/json + +{ + "names": "브리,토미,브라운", + "count": 10 +} + +### 게임 조회 + +GET http://localhost:8080/plays +Content-Type: application/json From 687c01892b2273d94b597c2bbe687a1af681f163 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 14:52:05 +0900 Subject: [PATCH 05/36] =?UTF-8?q?refactor:=20DB=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/CarDao.java | 8 ---- src/main/java/racingcar/dao/GameDao.java | 4 +- src/main/java/racingcar/dao/GameJdbcDao.java | 11 +++-- src/main/java/racingcar/dao/PlayerDao.java | 8 ++++ .../{CarJdbcDao.java => PlayerJdbcDao.java} | 23 ++++----- src/main/java/racingcar/entity/Game.java | 31 ++++++++++++ src/main/java/racingcar/entity/Player.java | 47 +++++++++++++++++++ .../racingcar/service/RacingGameService.java | 31 +++++++----- src/main/resources/data.sql | 20 ++++---- .../java/racingcar/dao/GameJdbcDaoTest.java | 6 +-- ...dbcDaoTest.java => PlayerJdbcDaoTest.java} | 26 +++++----- .../service/RacingGameServiceTest.java | 4 +- 12 files changed, 154 insertions(+), 65 deletions(-) delete mode 100644 src/main/java/racingcar/dao/CarDao.java create mode 100644 src/main/java/racingcar/dao/PlayerDao.java rename src/main/java/racingcar/dao/{CarJdbcDao.java => PlayerJdbcDao.java} (56%) create mode 100644 src/main/java/racingcar/entity/Game.java create mode 100644 src/main/java/racingcar/entity/Player.java rename src/test/java/racingcar/dao/{CarJdbcDaoTest.java => PlayerJdbcDaoTest.java} (56%) diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java deleted file mode 100644 index 728f1a298..000000000 --- a/src/main/java/racingcar/dao/CarDao.java +++ /dev/null @@ -1,8 +0,0 @@ -package racingcar.dao; - -import java.util.List; -import racingcar.domain.Car; - -public interface CarDao { - void saveAll(final int gameId, final List cars); -} diff --git a/src/main/java/racingcar/dao/GameDao.java b/src/main/java/racingcar/dao/GameDao.java index 66ed986fe..7d9498fb8 100644 --- a/src/main/java/racingcar/dao/GameDao.java +++ b/src/main/java/racingcar/dao/GameDao.java @@ -1,5 +1,7 @@ package racingcar.dao; +import racingcar.entity.Game; + public interface GameDao { - int save(final int trialCount, final String winners); + int saveAndGetId(final Game game); } diff --git a/src/main/java/racingcar/dao/GameJdbcDao.java b/src/main/java/racingcar/dao/GameJdbcDao.java index 5d7c69435..56e466394 100644 --- a/src/main/java/racingcar/dao/GameJdbcDao.java +++ b/src/main/java/racingcar/dao/GameJdbcDao.java @@ -1,10 +1,12 @@ package racingcar.dao; import java.sql.PreparedStatement; +import java.sql.Timestamp; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Component; +import racingcar.entity.Game; @Component public class GameJdbcDao implements GameDao { @@ -14,14 +16,15 @@ public GameJdbcDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - public int save(final int trialCount, final String winners) { - final String sql = "insert into game (trial, winners) values (?,?)"; + @Override + public int saveAndGetId(final Game game) { + final String sql = "insert into game (trial, created_at) values (?, ?)"; final KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(connection -> { PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"}); - ps.setInt(1, trialCount); - ps.setString(2, winners); + ps.setInt(1, game.getTrial()); + ps.setTimestamp(2, Timestamp.valueOf(game.getCreatedAt())); return ps; }, keyHolder); diff --git a/src/main/java/racingcar/dao/PlayerDao.java b/src/main/java/racingcar/dao/PlayerDao.java new file mode 100644 index 000000000..2d93d2153 --- /dev/null +++ b/src/main/java/racingcar/dao/PlayerDao.java @@ -0,0 +1,8 @@ +package racingcar.dao; + +import java.util.List; +import racingcar.entity.Player; + +public interface PlayerDao { + void saveAll(final List players); +} diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/PlayerJdbcDao.java similarity index 56% rename from src/main/java/racingcar/dao/CarJdbcDao.java rename to src/main/java/racingcar/dao/PlayerJdbcDao.java index dd1edf5cb..5270bf8bc 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/PlayerJdbcDao.java @@ -6,31 +6,32 @@ import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; -import racingcar.domain.Car; +import racingcar.entity.Player; @Component -public class CarJdbcDao implements CarDao { +public class PlayerJdbcDao implements PlayerDao { private final JdbcTemplate jdbcTemplate; - public CarJdbcDao(final JdbcTemplate jdbcTemplate) { + public PlayerJdbcDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - - public void saveAll(final int gameId, final List cars) { - final String sql = "INSERT INTO car(name, position, game_id) VALUES (?, ?, ?)"; + + public void saveAll(final List players) { + final String sql = "INSERT INTO player(name, position, winner, game_id) VALUES (?, ?, ?, ?)"; final BatchPreparedStatementSetter batchPreparedStatementSetter = new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { - final Car car = cars.get(i); - ps.setString(1, car.getName()); - ps.setInt(2, car.getPosition()); - ps.setInt(3, gameId); + final Player player = players.get(i); + ps.setString(1, player.getName()); + ps.setInt(2, player.getPosition()); + ps.setBoolean(3, player.isWinner()); + ps.setInt(4, player.getGameId()); } @Override public int getBatchSize() { - return cars.size(); + return players.size(); } }; jdbcTemplate.batchUpdate(sql, batchPreparedStatementSetter); diff --git a/src/main/java/racingcar/entity/Game.java b/src/main/java/racingcar/entity/Game.java new file mode 100644 index 000000000..602544094 --- /dev/null +++ b/src/main/java/racingcar/entity/Game.java @@ -0,0 +1,31 @@ +package racingcar.entity; + +import java.time.LocalDateTime; + +public class Game { + private final Integer id; + private final int trial; + private final LocalDateTime createdAt; + + public Game(final int trial) { + this(null, trial, LocalDateTime.now()); + } + + public Game(final Integer id, final int trial, final LocalDateTime createdAt) { + this.id = id; + this.trial = trial; + this.createdAt = createdAt; + } + + public Integer getId() { + return id; + } + + public int getTrial() { + return trial; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } +} diff --git a/src/main/java/racingcar/entity/Player.java b/src/main/java/racingcar/entity/Player.java new file mode 100644 index 000000000..a89b32a82 --- /dev/null +++ b/src/main/java/racingcar/entity/Player.java @@ -0,0 +1,47 @@ +package racingcar.entity; + +import racingcar.domain.Car; + +public class Player { + private final Integer id; + private final String name; + private final int position; + private final boolean winner; + private final Integer gameId; + + public Player(final String name, final int position, final boolean winner, final Integer gameId) { + this(null, name, position, winner, gameId); + } + + public Player(final Integer id, final String name, final int position, final boolean winner, final Integer gameId) { + this.id = id; + this.name = name; + this.position = position; + this.winner = winner; + this.gameId = gameId; + } + + public static Player of(final Car car, final boolean winner, final int gameId) { + return new Player(car.getName(), car.getPosition(), winner, gameId); + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + + public boolean isWinner() { + return winner; + } + + public Integer getGameId() { + return gameId; + } +} diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 49ecf1f29..f8f2de377 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -1,17 +1,22 @@ package racingcar.service; +import static java.util.stream.Collectors.toList; + import java.util.Arrays; +import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; import org.springframework.stereotype.Service; -import racingcar.dao.CarDao; import racingcar.dao.GameDao; +import racingcar.dao.PlayerDao; import racingcar.domain.Car; import racingcar.domain.NumberGenerator; import racingcar.domain.RacingGame; import racingcar.dto.CarDto; import racingcar.dto.GameRequest; import racingcar.dto.GameResponse; +import racingcar.entity.Game; +import racingcar.entity.Player; @Service public class RacingGameService { @@ -19,29 +24,33 @@ public class RacingGameService { private final NumberGenerator numberGenerator; private final GameDao gameDao; - private final CarDao carDao; + private final PlayerDao playerDao; - public RacingGameService(final NumberGenerator numberGenerator, final GameDao gameDao, final CarDao carDao) { + public RacingGameService(final NumberGenerator numberGenerator, final GameDao gameDao, final PlayerDao carDao) { this.numberGenerator = numberGenerator; this.gameDao = gameDao; - this.carDao = carDao; + this.playerDao = carDao; } public GameResponse play(final GameRequest gameRequest) { final RacingGame racingGame = playRacingGame(gameRequest); - final String winners = String.join(DELIMITER, racingGame.findWinners()); - final int gameId = gameDao.save(gameRequest.getCount(), winners); + final Game game = new Game(gameRequest.getCount()); + final int gameId = gameDao.saveAndGetId(game); final List cars = racingGame.findCurrentCarPositions(); - carDao.saveAll(gameId, cars); + final Set winners = new HashSet<>(racingGame.findWinners()); + final List players = cars.stream() + .map(car -> Player.of(car, winners.contains(car.getName()), gameId)) + .collect(toList()); + playerDao.saveAll(players); - return toGameResponse(winners, cars); + return toGameResponse(String.join(DELIMITER, racingGame.findWinners()), cars); } private RacingGame playRacingGame(final GameRequest gameRequest) { final List names = Arrays.stream(gameRequest.getNames().split(DELIMITER)) - .collect(Collectors.toList()); + .collect(toList()); final RacingGame racingGame = new RacingGame(numberGenerator, names, gameRequest.getCount()); while (racingGame.isPlayable()) { racingGame.play(); @@ -52,7 +61,7 @@ private RacingGame playRacingGame(final GameRequest gameRequest) { private GameResponse toGameResponse(final String winners, final List cars) { final List carDtos = cars.stream() .map(car -> new CarDto(car.getName(), car.getPosition())) - .collect(Collectors.toList()); + .collect(toList()); return new GameResponse(winners, carDtos); } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index a3a130798..21e35c5ae 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,19 +1,19 @@ -- TODO: 기능 구현에 필요한 내용을 추가하거나 수정하세요. CREATE TABLE GAME ( - id INT NOT NULL AUTO_INCREMENT, - winners VARCHAR(50) NOT NULL, - trial INT NOT NULL, - created_at DATETIME NOT NULL default current_timestamp, + id INT NOT NULL AUTO_INCREMENT, + trial INT NOT NULL, + created_at DATETIME NOT NULL, PRIMARY KEY (id) ); -CREATE TABLE CAR +CREATE TABLE PLAYER ( - id INT NOT NULL AUTO_INCREMENT, - name VARCHAR(50) NOT NULL, - position INT NOT NULL, - game_id INT NOT NULL, - FOREIGN KEY (game_id) REFERENCES GAME(id), + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + position INT NOT NULL, + winner BOOL NOT NULL, + game_id INT NOT NULL, + FOREIGN KEY (game_id) REFERENCES GAME (id), PRIMARY KEY (id) ) diff --git a/src/test/java/racingcar/dao/GameJdbcDaoTest.java b/src/test/java/racingcar/dao/GameJdbcDaoTest.java index 6e380bd0b..02df64298 100644 --- a/src/test/java/racingcar/dao/GameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/GameJdbcDaoTest.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; +import racingcar.entity.Game; @JdbcTest @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @@ -28,11 +29,10 @@ void setUp() { @Test void 게임을_저장한다() { // given - final int trialCount = 5; - final String winners = "비버"; + final Game game = new Game(5); // when - final int id = gameDao.save(trialCount, winners); + final int id = gameDao.saveAndGetId(game); // then assertThat(id).isPositive(); diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/PlayerJdbcDaoTest.java similarity index 56% rename from src/test/java/racingcar/dao/CarJdbcDaoTest.java rename to src/test/java/racingcar/dao/PlayerJdbcDaoTest.java index 0082ea71e..d5beccdcc 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/PlayerJdbcDaoTest.java @@ -3,7 +3,6 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.List; -import org.assertj.core.util.Lists; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -11,41 +10,38 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; -import racingcar.domain.Cars; -import racingcar.domain.NumberGenerator; -import racingcar.utils.TestNumberGenerator; +import racingcar.entity.Game; +import racingcar.entity.Player; @JdbcTest @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -public class CarJdbcDaoTest { +public class PlayerJdbcDaoTest { @Autowired private JdbcTemplate jdbcTemplate; - private CarDao carDao; + private PlayerDao playerDao; private GameDao gameDao; @BeforeEach void setUp() { - carDao = new CarJdbcDao(jdbcTemplate); + playerDao = new PlayerJdbcDao(jdbcTemplate); gameDao = new GameJdbcDao(jdbcTemplate); } @Test - void 입력받은_자동차를_전부_저장한다() { + void 입력받은_플레이어를_전부_저장한다() { // given - final Cars cars = new Cars(List.of("car1", "car2")); - NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3)); - cars.race(numberGenerator); - final int gameId = gameDao.save(3, "car1"); + final int gameId = gameDao.saveAndGetId(new Game(3)); + final List players = List.of(new Player("car1", 1, true, gameId)); // when - carDao.saveAll(gameId, cars.getCars()); + playerDao.saveAll(players); // then - final String sql = "select count(*) from car"; + final String sql = "select count(*) from player"; final int count = jdbcTemplate.queryForObject(sql, Integer.class); - assertThat(count).isEqualTo(2); + assertThat(count).isEqualTo(1); } } diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 866a806ba..4bc0bfd98 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -8,8 +8,8 @@ import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import racingcar.dao.CarDao; import racingcar.dao.GameDao; +import racingcar.dao.PlayerDao; import racingcar.domain.NumberGenerator; import racingcar.dto.GameRequest; import racingcar.dto.GameResponse; @@ -23,7 +23,7 @@ class RacingGameServiceTest { void 자동차_경주를_진행한다() { // given final GameDao gameDao = Mockito.mock(GameDao.class); - final CarDao carDao = Mockito.mock(CarDao.class); + final PlayerDao carDao = Mockito.mock(PlayerDao.class); final NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 3)); final RacingGameService racingGameService = new RacingGameService(numberGenerator, gameDao, carDao); final GameRequest gameRequest = new GameRequest("브리,비버,허브", 1); From 0254c3a8855544264606502358762328fbb534b6 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 15:10:15 +0900 Subject: [PATCH 06/36] =?UTF-8?q?refactor:=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EB=B0=98=EB=B3=B5=EC=A7=84=ED=96=89=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20=EA=B2=8C=EC=9E=84=20=EB=82=B4=EB=B6=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RacingGameController.java | 8 +++----- src/main/java/racingcar/domain/RacingGame.java | 10 ++++------ .../racingcar/service/RacingGameService.java | 4 +--- .../java/racingcar/domain/RacingGameTest.java | 18 ++++-------------- 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameController.java index 7081e2bf2..fa97e9827 100644 --- a/src/main/java/racingcar/controller/RacingGameController.java +++ b/src/main/java/racingcar/controller/RacingGameController.java @@ -41,11 +41,9 @@ private RacingGame initialize() { private void play(final RacingGame racingGame) { outputView.printResultMessage(); - while (racingGame.isPlayable()) { - racingGame.play(); - List cars = racingGame.findCurrentCarPositions(); - outputView.printCurrentCarPositions(cars); - } + racingGame.play(); + List cars = racingGame.findCurrentCarPositions(); + outputView.printCurrentCarPositions(cars); } private void findWinners(final RacingGame racingGame) { diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java index 04d5fee0f..c0c575eb5 100644 --- a/src/main/java/racingcar/domain/RacingGame.java +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -14,13 +14,11 @@ public RacingGame(final NumberGenerator numberGenerator, final List name this.count = new Count(count); } - public boolean isPlayable() { - return count.isPlayable(); - } - public void play() { - cars.race(numberGenerator); - count.decrease(); + while (count.isPlayable()) { + cars.race(numberGenerator); + count.decrease(); + } } public List findCurrentCarPositions() { diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index f8f2de377..c1efbb0f2 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -52,9 +52,7 @@ private RacingGame playRacingGame(final GameRequest gameRequest) { final List names = Arrays.stream(gameRequest.getNames().split(DELIMITER)) .collect(toList()); final RacingGame racingGame = new RacingGame(numberGenerator, names, gameRequest.getCount()); - while (racingGame.isPlayable()) { - racingGame.play(); - } + racingGame.play(); return racingGame; } diff --git a/src/test/java/racingcar/domain/RacingGameTest.java b/src/test/java/racingcar/domain/RacingGameTest.java index bba877940..60d57af9a 100644 --- a/src/test/java/racingcar/domain/RacingGameTest.java +++ b/src/test/java/racingcar/domain/RacingGameTest.java @@ -10,12 +10,6 @@ class RacingGameTest { - private RacingGame generateRacingGame(final int count) { - NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3)); - List names = List.of("car1", "car2"); - return new RacingGame(numberGenerator, names, count); - } - @Test @DisplayName("play 메서드는 자동차 경주 게임을 진행한다.") void should_playGame_when_play() { @@ -26,14 +20,10 @@ void should_playGame_when_play() { assertThat(racingGame.findWinners()).containsExactly("car1"); } - @Test - @DisplayName("play 메서드 호출 시 진행 가능 횟수가 1 줄어든다.") - void should_decreaseCount_when_play() { - RacingGame racingGame = generateRacingGame(1); - - racingGame.play(); - - assertThat(racingGame.isPlayable()).isFalse(); + private RacingGame generateRacingGame(final int count) { + NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3)); + List names = List.of("car1", "car2"); + return new RacingGame(numberGenerator, names, count); } @Test From 787df6f05a23328eb0f85df03082b8673f63249c Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 21:45:46 +0900 Subject: [PATCH 07/36] =?UTF-8?q?refactor:=20=EC=9A=B0=EC=8A=B9=EC=9E=90?= =?UTF-8?q?=20=EB=B0=8F=20=EC=B0=B8=EA=B0=80=EC=9E=90=EB=A5=BC=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=98=95=ED=83=9C=EB=A1=9C=20=EB=B0=9B?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- http-request.http | 6 +- .../controller/WebRacingGameController.java | 6 +- src/main/java/racingcar/dao/CarDao.java | 8 ++ .../{PlayerJdbcDao.java => CarJdbcDao.java} | 12 +- src/main/java/racingcar/dao/GameDao.java | 4 +- src/main/java/racingcar/dao/GameJdbcDao.java | 4 +- src/main/java/racingcar/dao/PlayerDao.java | 8 -- .../{GameRequest.java => GameRequestDto.java} | 10 +- ...GameResponse.java => GameResponseDto.java} | 8 +- .../entity/{Player.java => CarEntity.java} | 16 ++- .../entity/{Game.java => GameEntity.java} | 6 +- .../racingcar/service/RacingGameService.java | 41 +++--- src/main/resources/static/index.html | 124 +++++++++--------- .../WebRacingGameControllerTest.java | 14 +- ...erJdbcDaoTest.java => CarJdbcDaoTest.java} | 16 +-- .../java/racingcar/dao/GameJdbcDaoTest.java | 4 +- .../service/RacingGameServiceTest.java | 15 ++- 17 files changed, 155 insertions(+), 147 deletions(-) create mode 100644 src/main/java/racingcar/dao/CarDao.java rename src/main/java/racingcar/dao/{PlayerJdbcDao.java => CarJdbcDao.java} (81%) delete mode 100644 src/main/java/racingcar/dao/PlayerDao.java rename src/main/java/racingcar/dto/{GameRequest.java => GameRequestDto.java} (50%) rename src/main/java/racingcar/dto/{GameResponse.java => GameResponseDto.java} (58%) rename src/main/java/racingcar/entity/{Player.java => CarEntity.java} (60%) rename src/main/java/racingcar/entity/{Game.java => GameEntity.java} (76%) rename src/test/java/racingcar/dao/{PlayerJdbcDaoTest.java => CarJdbcDaoTest.java} (74%) diff --git a/http-request.http b/http-request.http index 93c9bea86..4e2a7c302 100644 --- a/http-request.http +++ b/http-request.http @@ -4,7 +4,11 @@ POST localhost:8080/plays Content-Type: application/json { - "names": "브리,토미,브라운", + "names": [ + "브리", + "토미", + "브라운" + ], "count": 10 } diff --git a/src/main/java/racingcar/controller/WebRacingGameController.java b/src/main/java/racingcar/controller/WebRacingGameController.java index 00be7fb3d..27d9429a4 100644 --- a/src/main/java/racingcar/controller/WebRacingGameController.java +++ b/src/main/java/racingcar/controller/WebRacingGameController.java @@ -3,8 +3,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import racingcar.dto.GameRequest; -import racingcar.dto.GameResponse; +import racingcar.dto.GameRequestDto; +import racingcar.dto.GameResponseDto; import racingcar.service.RacingGameService; @RestController @@ -16,7 +16,7 @@ public WebRacingGameController(final RacingGameService racingGameService) { } @PostMapping("/plays") - public GameResponse plays(@RequestBody final GameRequest gameRequest) { + public GameResponseDto plays(@RequestBody final GameRequestDto gameRequest) { return racingGameService.play(gameRequest); } } diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java new file mode 100644 index 000000000..c9a13ae6e --- /dev/null +++ b/src/main/java/racingcar/dao/CarDao.java @@ -0,0 +1,8 @@ +package racingcar.dao; + +import java.util.List; +import racingcar.entity.CarEntity; + +public interface CarDao { + void saveAll(final List players); +} diff --git a/src/main/java/racingcar/dao/PlayerJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java similarity index 81% rename from src/main/java/racingcar/dao/PlayerJdbcDao.java rename to src/main/java/racingcar/dao/CarJdbcDao.java index 5270bf8bc..09a463405 100644 --- a/src/main/java/racingcar/dao/PlayerJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -6,23 +6,23 @@ import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; -import racingcar.entity.Player; +import racingcar.entity.CarEntity; @Component -public class PlayerJdbcDao implements PlayerDao { +public class CarJdbcDao implements CarDao { private final JdbcTemplate jdbcTemplate; - public PlayerJdbcDao(final JdbcTemplate jdbcTemplate) { + public CarJdbcDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - - public void saveAll(final List players) { + + public void saveAll(final List players) { final String sql = "INSERT INTO player(name, position, winner, game_id) VALUES (?, ?, ?, ?)"; final BatchPreparedStatementSetter batchPreparedStatementSetter = new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { - final Player player = players.get(i); + final CarEntity player = players.get(i); ps.setString(1, player.getName()); ps.setInt(2, player.getPosition()); ps.setBoolean(3, player.isWinner()); diff --git a/src/main/java/racingcar/dao/GameDao.java b/src/main/java/racingcar/dao/GameDao.java index 7d9498fb8..c3b0de16d 100644 --- a/src/main/java/racingcar/dao/GameDao.java +++ b/src/main/java/racingcar/dao/GameDao.java @@ -1,7 +1,7 @@ package racingcar.dao; -import racingcar.entity.Game; +import racingcar.entity.GameEntity; public interface GameDao { - int saveAndGetId(final Game game); + int saveAndGetId(final GameEntity game); } diff --git a/src/main/java/racingcar/dao/GameJdbcDao.java b/src/main/java/racingcar/dao/GameJdbcDao.java index 56e466394..e07254adf 100644 --- a/src/main/java/racingcar/dao/GameJdbcDao.java +++ b/src/main/java/racingcar/dao/GameJdbcDao.java @@ -6,7 +6,7 @@ import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Component; -import racingcar.entity.Game; +import racingcar.entity.GameEntity; @Component public class GameJdbcDao implements GameDao { @@ -17,7 +17,7 @@ public GameJdbcDao(final JdbcTemplate jdbcTemplate) { } @Override - public int saveAndGetId(final Game game) { + public int saveAndGetId(final GameEntity game) { final String sql = "insert into game (trial, created_at) values (?, ?)"; final KeyHolder keyHolder = new GeneratedKeyHolder(); diff --git a/src/main/java/racingcar/dao/PlayerDao.java b/src/main/java/racingcar/dao/PlayerDao.java deleted file mode 100644 index 2d93d2153..000000000 --- a/src/main/java/racingcar/dao/PlayerDao.java +++ /dev/null @@ -1,8 +0,0 @@ -package racingcar.dao; - -import java.util.List; -import racingcar.entity.Player; - -public interface PlayerDao { - void saveAll(final List players); -} diff --git a/src/main/java/racingcar/dto/GameRequest.java b/src/main/java/racingcar/dto/GameRequestDto.java similarity index 50% rename from src/main/java/racingcar/dto/GameRequest.java rename to src/main/java/racingcar/dto/GameRequestDto.java index 8eae1b204..ff9ec0466 100644 --- a/src/main/java/racingcar/dto/GameRequest.java +++ b/src/main/java/racingcar/dto/GameRequestDto.java @@ -1,15 +1,17 @@ package racingcar.dto; -public class GameRequest { - private final String names; +import java.util.List; + +public class GameRequestDto { + private final List names; private final int count; - public GameRequest(final String names, final int count) { + public GameRequestDto(final List names, final int count) { this.names = names; this.count = count; } - public String getNames() { + public List getNames() { return names; } diff --git a/src/main/java/racingcar/dto/GameResponse.java b/src/main/java/racingcar/dto/GameResponseDto.java similarity index 58% rename from src/main/java/racingcar/dto/GameResponse.java rename to src/main/java/racingcar/dto/GameResponseDto.java index 2cbdbcf32..389c330a6 100644 --- a/src/main/java/racingcar/dto/GameResponse.java +++ b/src/main/java/racingcar/dto/GameResponseDto.java @@ -2,16 +2,16 @@ import java.util.List; -public class GameResponse { - private final String winners; +public class GameResponseDto { + private final List winners; private final List racingCars; - public GameResponse(final String winners, final List racingCars) { + public GameResponseDto(final List winners, final List racingCars) { this.winners = winners; this.racingCars = racingCars; } - public String getWinners() { + public List getWinners() { return winners; } diff --git a/src/main/java/racingcar/entity/Player.java b/src/main/java/racingcar/entity/CarEntity.java similarity index 60% rename from src/main/java/racingcar/entity/Player.java rename to src/main/java/racingcar/entity/CarEntity.java index a89b32a82..c2ab2c1a0 100644 --- a/src/main/java/racingcar/entity/Player.java +++ b/src/main/java/racingcar/entity/CarEntity.java @@ -2,18 +2,24 @@ import racingcar.domain.Car; -public class Player { +public class CarEntity { private final Integer id; private final String name; private final int position; private final boolean winner; private final Integer gameId; - public Player(final String name, final int position, final boolean winner, final Integer gameId) { + public CarEntity(final String name, final int position, final boolean winner, final Integer gameId) { this(null, name, position, winner, gameId); } - public Player(final Integer id, final String name, final int position, final boolean winner, final Integer gameId) { + public CarEntity( + final Integer id, + final String name, + final int position, + final boolean winner, + final Integer gameId + ) { this.id = id; this.name = name; this.position = position; @@ -21,8 +27,8 @@ public Player(final Integer id, final String name, final int position, final boo this.gameId = gameId; } - public static Player of(final Car car, final boolean winner, final int gameId) { - return new Player(car.getName(), car.getPosition(), winner, gameId); + public static CarEntity of(final Car car, final boolean winner, final int gameId) { + return new CarEntity(car.getName(), car.getPosition(), winner, gameId); } public Integer getId() { diff --git a/src/main/java/racingcar/entity/Game.java b/src/main/java/racingcar/entity/GameEntity.java similarity index 76% rename from src/main/java/racingcar/entity/Game.java rename to src/main/java/racingcar/entity/GameEntity.java index 602544094..8451b9429 100644 --- a/src/main/java/racingcar/entity/Game.java +++ b/src/main/java/racingcar/entity/GameEntity.java @@ -2,16 +2,16 @@ import java.time.LocalDateTime; -public class Game { +public class GameEntity { private final Integer id; private final int trial; private final LocalDateTime createdAt; - public Game(final int trial) { + public GameEntity(final int trial) { this(null, trial, LocalDateTime.now()); } - public Game(final Integer id, final int trial, final LocalDateTime createdAt) { + public GameEntity(final Integer id, final int trial, final LocalDateTime createdAt) { this.id = id; this.trial = trial; this.createdAt = createdAt; diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index c1efbb0f2..6966290fe 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -2,64 +2,59 @@ import static java.util.stream.Collectors.toList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.springframework.stereotype.Service; +import racingcar.dao.CarDao; import racingcar.dao.GameDao; -import racingcar.dao.PlayerDao; import racingcar.domain.Car; import racingcar.domain.NumberGenerator; import racingcar.domain.RacingGame; import racingcar.dto.CarDto; -import racingcar.dto.GameRequest; -import racingcar.dto.GameResponse; -import racingcar.entity.Game; -import racingcar.entity.Player; +import racingcar.dto.GameRequestDto; +import racingcar.dto.GameResponseDto; +import racingcar.entity.CarEntity; +import racingcar.entity.GameEntity; @Service public class RacingGameService { - private static final String DELIMITER = ","; - private final NumberGenerator numberGenerator; private final GameDao gameDao; - private final PlayerDao playerDao; + private final CarDao carDao; - public RacingGameService(final NumberGenerator numberGenerator, final GameDao gameDao, final PlayerDao carDao) { + public RacingGameService(final NumberGenerator numberGenerator, final GameDao gameDao, final CarDao carDao) { this.numberGenerator = numberGenerator; this.gameDao = gameDao; - this.playerDao = carDao; + this.carDao = carDao; } - public GameResponse play(final GameRequest gameRequest) { + public GameResponseDto play(final GameRequestDto gameRequest) { final RacingGame racingGame = playRacingGame(gameRequest); - final Game game = new Game(gameRequest.getCount()); + final GameEntity game = new GameEntity(gameRequest.getCount()); final int gameId = gameDao.saveAndGetId(game); final List cars = racingGame.findCurrentCarPositions(); final Set winners = new HashSet<>(racingGame.findWinners()); - final List players = cars.stream() - .map(car -> Player.of(car, winners.contains(car.getName()), gameId)) + final List players = cars.stream() + .map(car -> CarEntity.of(car, winners.contains(car.getName()), gameId)) .collect(toList()); - playerDao.saveAll(players); + carDao.saveAll(players); - return toGameResponse(String.join(DELIMITER, racingGame.findWinners()), cars); + return toGameResponse(racingGame.findWinners(), cars); } - private RacingGame playRacingGame(final GameRequest gameRequest) { - final List names = Arrays.stream(gameRequest.getNames().split(DELIMITER)) - .collect(toList()); - final RacingGame racingGame = new RacingGame(numberGenerator, names, gameRequest.getCount()); + private RacingGame playRacingGame(final GameRequestDto gameRequest) { + final RacingGame racingGame = new RacingGame(numberGenerator, gameRequest.getNames(), gameRequest.getCount()); racingGame.play(); return racingGame; } - private GameResponse toGameResponse(final String winners, final List cars) { + private GameResponseDto toGameResponse(final List winners, final List cars) { final List carDtos = cars.stream() .map(car -> new CarDto(car.getName(), car.getPosition())) .collect(toList()); - return new GameResponse(winners, carDtos); + return new GameResponseDto(winners, carDtos); } } diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index c731a65ff..5e0b7134d 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -1,83 +1,83 @@ - - 자동차 경주 게임 - + + 자동차 경주 게임 +
-

Racing Car

-
- - -
+

Racing Car

+
+ + +

자동차 경주 게임

-
- - +
+ + - - -
+ + +
- + -
- 우승자: -
- 결과: -
-
+
+ 우승자: +
+ 결과: +
+
diff --git a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java index 559bcb0c2..d92ffa446 100644 --- a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java @@ -20,8 +20,8 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; import racingcar.dto.CarDto; -import racingcar.dto.GameRequest; -import racingcar.dto.GameResponse; +import racingcar.dto.GameRequestDto; +import racingcar.dto.GameResponseDto; import racingcar.service.RacingGameService; @WebMvcTest @@ -41,13 +41,13 @@ class WebRacingGameControllerTest { @Test void 게임을_진행한다() throws Exception { // given - final GameRequest gameRequest = new GameRequest("비버,허브", 1); + final GameRequestDto gameRequest = new GameRequestDto(List.of("비버", "허브"), 1); final String request = objectMapper.writeValueAsString(gameRequest); - final GameResponse gameResponse = new GameResponse( - "비버", + final GameResponseDto gameResponse = new GameResponseDto( + List.of("비버"), List.of(new CarDto("비버", 1), new CarDto("허브", 0)) ); - given(racingGameService.play(any(GameRequest.class))) + given(racingGameService.play(any(GameRequestDto.class))) .willReturn(gameResponse); // expect @@ -55,7 +55,7 @@ class WebRacingGameControllerTest { .content(request) .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) - .andExpect(jsonPath("winners").value("비버")) + .andExpect(jsonPath("$.winners[0]").value("비버")) .andExpect(jsonPath("racingCars", hasSize(2))) .andExpect(jsonPath("$.racingCars[0].name").value("비버")) .andExpect(jsonPath("$.racingCars[0].position").value(1)) diff --git a/src/test/java/racingcar/dao/PlayerJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java similarity index 74% rename from src/test/java/racingcar/dao/PlayerJdbcDaoTest.java rename to src/test/java/racingcar/dao/CarJdbcDaoTest.java index d5beccdcc..c0c18f5a4 100644 --- a/src/test/java/racingcar/dao/PlayerJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -10,34 +10,34 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; -import racingcar.entity.Game; -import racingcar.entity.Player; +import racingcar.entity.CarEntity; +import racingcar.entity.GameEntity; @JdbcTest @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -public class PlayerJdbcDaoTest { +public class CarJdbcDaoTest { @Autowired private JdbcTemplate jdbcTemplate; - private PlayerDao playerDao; + private CarDao carDao; private GameDao gameDao; @BeforeEach void setUp() { - playerDao = new PlayerJdbcDao(jdbcTemplate); + carDao = new CarJdbcDao(jdbcTemplate); gameDao = new GameJdbcDao(jdbcTemplate); } @Test void 입력받은_플레이어를_전부_저장한다() { // given - final int gameId = gameDao.saveAndGetId(new Game(3)); - final List players = List.of(new Player("car1", 1, true, gameId)); + final int gameId = gameDao.saveAndGetId(new GameEntity(3)); + final List players = List.of(new CarEntity("car1", 1, true, gameId)); // when - playerDao.saveAll(players); + carDao.saveAll(players); // then final String sql = "select count(*) from player"; diff --git a/src/test/java/racingcar/dao/GameJdbcDaoTest.java b/src/test/java/racingcar/dao/GameJdbcDaoTest.java index 02df64298..ea5a64081 100644 --- a/src/test/java/racingcar/dao/GameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/GameJdbcDaoTest.java @@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; -import racingcar.entity.Game; +import racingcar.entity.GameEntity; @JdbcTest @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @@ -29,7 +29,7 @@ void setUp() { @Test void 게임을_저장한다() { // given - final Game game = new Game(5); + final GameEntity game = new GameEntity(5); // when final int id = gameDao.saveAndGetId(game); diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 4bc0bfd98..b33de4c43 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -3,16 +3,17 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import java.util.List; import org.assertj.core.util.Lists; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import racingcar.dao.CarDao; import racingcar.dao.GameDao; -import racingcar.dao.PlayerDao; import racingcar.domain.NumberGenerator; -import racingcar.dto.GameRequest; -import racingcar.dto.GameResponse; +import racingcar.dto.GameRequestDto; +import racingcar.dto.GameResponseDto; import racingcar.utils.TestNumberGenerator; @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @@ -23,17 +24,17 @@ class RacingGameServiceTest { void 자동차_경주를_진행한다() { // given final GameDao gameDao = Mockito.mock(GameDao.class); - final PlayerDao carDao = Mockito.mock(PlayerDao.class); + final CarDao carDao = Mockito.mock(CarDao.class); final NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 3)); final RacingGameService racingGameService = new RacingGameService(numberGenerator, gameDao, carDao); - final GameRequest gameRequest = new GameRequest("브리,비버,허브", 1); + final GameRequestDto gameRequest = new GameRequestDto(List.of("브리", "비버", "허브"), 1); // when - final GameResponse gameResponse = racingGameService.play(gameRequest); + final GameResponseDto gameResponse = racingGameService.play(gameRequest); // then assertAll( - () -> assertThat(gameResponse.getWinners()).isEqualTo("브리"), + () -> assertThat(gameResponse.getWinners()).containsExactly("브리"), () -> assertThat(gameResponse.getRacingCars()).hasSize(3) ); } From c76a89edf85f297b5d5da8dd4c3875a0b4c70ade Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 21:50:02 +0900 Subject: [PATCH 08/36] =?UTF-8?q?refactor:=20=EC=B0=B8=EA=B0=80=ED=95=9C?= =?UTF-8?q?=20=EC=9E=90=EB=8F=99=EC=B0=A8=EB=A5=BC=20=EC=A0=84=EB=B6=80=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/controller/RacingGameController.java | 2 +- src/main/java/racingcar/domain/RacingGame.java | 2 +- src/main/java/racingcar/service/RacingGameService.java | 2 +- src/test/java/racingcar/domain/RacingGameTest.java | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameController.java index fa97e9827..b1fd969f6 100644 --- a/src/main/java/racingcar/controller/RacingGameController.java +++ b/src/main/java/racingcar/controller/RacingGameController.java @@ -42,7 +42,7 @@ private RacingGame initialize() { private void play(final RacingGame racingGame) { outputView.printResultMessage(); racingGame.play(); - List cars = racingGame.findCurrentCarPositions(); + List cars = racingGame.getCars(); outputView.printCurrentCarPositions(cars); } diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java index c0c575eb5..c135b6b1d 100644 --- a/src/main/java/racingcar/domain/RacingGame.java +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -21,7 +21,7 @@ public void play() { } } - public List findCurrentCarPositions() { + public List getCars() { return cars.getCars(); } diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 6966290fe..e332589f4 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -35,7 +35,7 @@ public GameResponseDto play(final GameRequestDto gameRequest) { final GameEntity game = new GameEntity(gameRequest.getCount()); final int gameId = gameDao.saveAndGetId(game); - final List cars = racingGame.findCurrentCarPositions(); + final List cars = racingGame.getCars(); final Set winners = new HashSet<>(racingGame.findWinners()); final List players = cars.stream() .map(car -> CarEntity.of(car, winners.contains(car.getName()), gameId)) diff --git a/src/test/java/racingcar/domain/RacingGameTest.java b/src/test/java/racingcar/domain/RacingGameTest.java index 60d57af9a..6551b2060 100644 --- a/src/test/java/racingcar/domain/RacingGameTest.java +++ b/src/test/java/racingcar/domain/RacingGameTest.java @@ -27,12 +27,12 @@ private RacingGame generateRacingGame(final int count) { } @Test - @DisplayName("findCurrentCarPositions 메서드는 현재 경주에 참가하는 자동차들의 이름과 위치를 반환한다.") + @DisplayName("getCars 메서드는 현재 경주에 참가하는 자동차들의 이름과 위치를 반환한다.") void should_returnCarList_when_findCurrentCarPositions() { RacingGame racingGame = generateRacingGame(1); racingGame.play(); - List result = racingGame.findCurrentCarPositions(); + List result = racingGame.getCars(); assertThat(result) .extracting(Car::getPosition) From 9ee855fc22c77731d61ee4497aa13179f8a33204 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 22:06:08 +0900 Subject: [PATCH 09/36] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=EB=AA=85=20=ED=95=9C=EA=B5=AD?= =?UTF-8?q?=EC=96=B4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WebRacingGameControllerTest.java | 4 ++- .../java/racingcar/dao/CarJdbcDaoTest.java | 4 ++- .../java/racingcar/dao/GameJdbcDaoTest.java | 4 ++- src/test/java/racingcar/domain/CarTest.java | 24 ++++++++++---- src/test/java/racingcar/domain/CarsTest.java | 33 +++++++++++++------ src/test/java/racingcar/domain/CountTest.java | 15 +++++++-- src/test/java/racingcar/domain/NameTest.java | 9 ++++- .../java/racingcar/domain/PositionTest.java | 16 ++++++--- .../java/racingcar/domain/RacingGameTest.java | 22 ++++++++++--- .../domain/RandomNumberGeneratorTest.java | 17 ---------- .../service/RacingGameServiceTest.java | 10 +++--- 11 files changed, 104 insertions(+), 54 deletions(-) delete mode 100644 src/test/java/racingcar/domain/RandomNumberGeneratorTest.java diff --git a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java index d92ffa446..be59a7721 100644 --- a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -25,6 +26,7 @@ import racingcar.service.RacingGameService; @WebMvcTest +@DisplayName("WebRacingGameController 클래스") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") class WebRacingGameControllerTest { @@ -39,7 +41,7 @@ class WebRacingGameControllerTest { private RacingGameService racingGameService; @Test - void 게임을_진행한다() throws Exception { + void play_메서드는_게임을_진행한다() throws Exception { // given final GameRequestDto gameRequest = new GameRequestDto(List.of("비버", "허브"), 1); final String request = objectMapper.writeValueAsString(gameRequest); diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index c0c18f5a4..2e448c8b8 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -4,6 +4,7 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -14,6 +15,7 @@ import racingcar.entity.GameEntity; @JdbcTest +@DisplayName("CarJdbcDao 클래스") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") public class CarJdbcDaoTest { @@ -31,7 +33,7 @@ void setUp() { } @Test - void 입력받은_플레이어를_전부_저장한다() { + void saveAll_메서드는_입력받은_플레이어를_전부_저장한다() { // given final int gameId = gameDao.saveAndGetId(new GameEntity(3)); final List players = List.of(new CarEntity("car1", 1, true, gameId)); diff --git a/src/test/java/racingcar/dao/GameJdbcDaoTest.java b/src/test/java/racingcar/dao/GameJdbcDaoTest.java index ea5a64081..e5565f31e 100644 --- a/src/test/java/racingcar/dao/GameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/GameJdbcDaoTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -12,6 +13,7 @@ import racingcar.entity.GameEntity; @JdbcTest +@DisplayName("GameJdbcDao 클래스") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") public class GameJdbcDaoTest { @@ -27,7 +29,7 @@ void setUp() { } @Test - void 게임을_저장한다() { + void saveAndGetId_메서드는_게임을_저장하고_저장한_게임의_id값을_반환한다() { // given final GameEntity game = new GameEntity(5); diff --git a/src/test/java/racingcar/domain/CarTest.java b/src/test/java/racingcar/domain/CarTest.java index cc3dd6d2c..8c9388cd6 100644 --- a/src/test/java/racingcar/domain/CarTest.java +++ b/src/test/java/racingcar/domain/CarTest.java @@ -3,40 +3,52 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +@DisplayName("Car 클래스") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") public class CarTest { - @ParameterizedTest(name = "move 메서드는 값을 입력받고 4이상인 경우 전진한다. 초기 위치: 0 입력값: {0} 동작 후 위치: {1}") + @ParameterizedTest(name = "move 메서드는 값을 입력받아 4이상인 경우 전진한다. 초기 위치: 0 입력값: {0} 동작 후 위치: {1}") @CsvSource(value = {"4,1", "3,0"}) - void should_move_when_valueIsMoreThanFour(final int value, final int position) { + void move_메서드는_값을_입력받아_4이상인_경우_전진한다(final int value, final int position) { + // given Car car = new Car("Herb"); + // when car.move(value); + // then assertThat(car.getPosition()).isEqualTo(position); } @Test - @DisplayName("isSamePosition 메서드는 위치가 다르면 false를 반환한다.") - void should_returnFalse_when_positionIsNotSame() { + void isSamePosition_메서드는_위치가_다르면_false를_반환한다() { + // given Car car = new Car("Herb"); car.move(Integer.MAX_VALUE); + // when boolean result = car.isSamePosition(new Car("Herb2")); + // then assertThat(result).isFalse(); } @Test - @DisplayName("isSamePosition 메서드는 위치가 같으면 true를 반환한다.") - void should_returnSame_when_positionIsSame() { + void isSamePosition_메서드는_위치가_같으면_true를_반환한다() { + // given Car car = new Car("Herb"); + // when boolean result = car.isSamePosition(new Car("Herb2")); + // then assertThat(result).isTrue(); } } diff --git a/src/test/java/racingcar/domain/CarsTest.java b/src/test/java/racingcar/domain/CarsTest.java index 6b8f6caf7..7ae907937 100644 --- a/src/test/java/racingcar/domain/CarsTest.java +++ b/src/test/java/racingcar/domain/CarsTest.java @@ -8,9 +8,14 @@ import org.assertj.core.util.Lists; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import racingcar.utils.TestNumberGenerator; +@DisplayName("Cars 클래스") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") public class CarsTest { private Cars cars; @@ -21,56 +26,64 @@ void setUp() { } @Test - @DisplayName("생성자는 중복된 이름이 존재하는 목록을 입력받으면 예외를 던진다.") - void should_throwException_when_inputDuplicatedNames() { + void 생성자는_중복된_이름이_존재하는_목록을_입력받으면_예외를_던진다() { + // given List duplicatedNames = List.of("car1", "car1"); + // expect assertThatThrownBy(() -> new Cars(duplicatedNames)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("중복된 차 이름이 없어야 합니다."); } @Test - @DisplayName("race 메서드는 자동차 경주를 1회 진행한다.") - void should_raceOneTime_when_race() { + void race_메서드는_자동차_경주를_1회_진행한다() { + // given NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 5)); + // when cars.race(numberGenerator); + // then assertThat(cars.getCars()) .extracting(Car::getPosition) .containsExactly(1, 0, 1); } @Test - @DisplayName("findWinners 메서드는 우승자 이름 목록을 반환한다.") - void should_returnWinnersName_when_findWinners() { + void findWinners_메서드는_우승자_이름_목록을_반환한다() { + // given NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 5)); cars.race(numberGenerator); + // when List result = cars.findWinners(); + // then assertThat(result).containsExactly("car1", "car3"); } @Test - @DisplayName("findWinners 메서드는 우승자가 존재하지 않는 경우 예외를 던진다.") - void should_throwException_when_emptyCars() { + void findWinners_메서드는_우승자가_존재하지_않는_경우_예외를_던진다() { + // given Cars emptyCars = new Cars(List.of()); + // expect assertThatThrownBy(emptyCars::findWinners) .isInstanceOf(IllegalArgumentException.class) .hasMessage("우승자가 존재하지 않습니다."); } @Test - @DisplayName("getCars 메서드는 경주에 참가한 모든 차의 이름과 현재 위치를 반환한다.") - void should_returnCurrentCarNameAndPositions_when_getCars() { + void getCars_메서드는_경주에_참가한_모든_차의_이름과_현재_위치를_반환한다() { + // given NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 5)); cars.race(numberGenerator); + // when List result = cars.getCars(); + // then assertAll( () -> assertThat(result).extracting(Car::getName).containsExactly("car1", "car2", "car3"), () -> assertThat(result).extracting(Car::getPosition).containsExactly(1, 0, 1) diff --git a/src/test/java/racingcar/domain/CountTest.java b/src/test/java/racingcar/domain/CountTest.java index aff750624..84c5d13fb 100644 --- a/src/test/java/racingcar/domain/CountTest.java +++ b/src/test/java/racingcar/domain/CountTest.java @@ -3,27 +3,36 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +@DisplayName("Count 클래스") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") class CountTest { @Test - @DisplayName("decrease 메서드는 횟수를 1 감소시킨다.") - void should_minusOne_when_decrease() { + void decrease_메서드는_횟수를_1_감소시킨다() { + // given Count count = new Count(1); + // when count.decrease(); + // then assertThat(count.isPlayable()).isFalse(); } @ParameterizedTest(name = "isPlayable 메서드는 진행 가능 횟수가 {0}인 경우 {1}을 반환한다.") @CsvSource({"1,true", "0,false"}) - void should_returnState_when_isPlayable(final int value, final boolean state) { + void isPlayable_메서드는_진행_가능_여부를_반환한다(final int value, final boolean state) { + // given Count count = new Count(value); + // expect assertThat(count.isPlayable()).isEqualTo(state); } } diff --git a/src/test/java/racingcar/domain/NameTest.java b/src/test/java/racingcar/domain/NameTest.java index 2d22d9bf0..b58f7b629 100644 --- a/src/test/java/racingcar/domain/NameTest.java +++ b/src/test/java/racingcar/domain/NameTest.java @@ -2,14 +2,21 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +@DisplayName("Name 클래스") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") public class NameTest { @ParameterizedTest(name = "생성자는 이름의 길이가 없거나 5보다 크다면 예외를 던진다. 입력값: {0}") @ValueSource(strings = {"dazzle", ""}) - void should_throwException_when_invalidNameLength(final String name) { + void 생성자는_이름의_길이가_없거나_5보다_크다면_예외를_던진다(final String name) { + // expect assertThatThrownBy(() -> new Name(name)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("차의 이름은 1자 이상, 5자 이하여야 합니다. 입력된 차 이름 : " + name); diff --git a/src/test/java/racingcar/domain/PositionTest.java b/src/test/java/racingcar/domain/PositionTest.java index 98186de20..411b9e29f 100644 --- a/src/test/java/racingcar/domain/PositionTest.java +++ b/src/test/java/racingcar/domain/PositionTest.java @@ -3,25 +3,33 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; +@DisplayName("Position 클래스") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") class PositionTest { @Test - @DisplayName("Position의 초기값은 0을 가진다.") - void should_initValueIsZero_when_createPosition() { + void Position의_초기_위치값은_0이다() { + // given Position position = new Position(); + // expect assertThat(position.getValue()).isEqualTo(0); } @Test - @DisplayName("increase 메서드는 위치 값을 1 증가시킨다.") - void should_plusOne_when_increase() { + void increase_메서드는_위치_값을_1_증가시킨다() { + // given Position position = new Position(); + // when position.increase(); + // then assertThat(position.getValue()).isEqualTo(1); } } diff --git a/src/test/java/racingcar/domain/RacingGameTest.java b/src/test/java/racingcar/domain/RacingGameTest.java index 6551b2060..0a00b8dd5 100644 --- a/src/test/java/racingcar/domain/RacingGameTest.java +++ b/src/test/java/racingcar/domain/RacingGameTest.java @@ -5,18 +5,26 @@ import java.util.List; import org.assertj.core.util.Lists; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import racingcar.utils.TestNumberGenerator; +@DisplayName("RacingGame 클래스") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") class RacingGameTest { @Test @DisplayName("play 메서드는 자동차 경주 게임을 진행한다.") - void should_playGame_when_play() { + void play_메서드를_호출하면_자동차_경주_게임을_진행한다() { + // given RacingGame racingGame = generateRacingGame(1); + // when racingGame.play(); + // then assertThat(racingGame.findWinners()).containsExactly("car1"); } @@ -27,26 +35,30 @@ private RacingGame generateRacingGame(final int count) { } @Test - @DisplayName("getCars 메서드는 현재 경주에 참가하는 자동차들의 이름과 위치를 반환한다.") - void should_returnCarList_when_findCurrentCarPositions() { + void getCars_메서드는_현재_경주에_참가하는_자동차들의_이름과_위치를_반환한다() { + // given RacingGame racingGame = generateRacingGame(1); racingGame.play(); + // when List result = racingGame.getCars(); + // then assertThat(result) .extracting(Car::getPosition) .containsExactly(1, 0); } @Test - @DisplayName("findWinners 메서드는 우승자의 이름목록을 반환한다.") - void should_returnWinnersName_when_findWinners() { + void findWinners_메서드는_우승자의_이름목록을_반환한다() { + // given RacingGame racingGame = generateRacingGame(1); racingGame.play(); + // when List result = racingGame.findWinners(); + // then assertThat(result).containsExactly("car1"); } } diff --git a/src/test/java/racingcar/domain/RandomNumberGeneratorTest.java b/src/test/java/racingcar/domain/RandomNumberGeneratorTest.java deleted file mode 100644 index 31f91b6f0..000000000 --- a/src/test/java/racingcar/domain/RandomNumberGeneratorTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package racingcar.domain; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.RepeatedTest; - -class RandomNumberGeneratorTest { - - @RepeatedTest(value = 10, name = "{displayName} repetition: {currentRepetition} of {totalRepetitions}") - @DisplayName("generate 메서드는 0 부터 9 사이의 숫자를 무작위로 생성한다.") - void should_returnNumber_betweenZeroToNine_when_generate() { - NumberGenerator numberGenerator = new RandomNumberGenerator(); - - assertThat(numberGenerator.generate()).isBetween(0, 9); - } -} diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index b33de4c43..3e56024ec 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -5,8 +5,9 @@ import java.util.List; import org.assertj.core.util.Lists; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import racingcar.dao.CarDao; @@ -16,12 +17,12 @@ import racingcar.dto.GameResponseDto; import racingcar.utils.TestNumberGenerator; -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -@SuppressWarnings("NonAsciiCharacters") +@DisplayName("RacingGameService 클래스") +@DisplayNameGeneration(ReplaceUnderscores.class) class RacingGameServiceTest { @Test - void 자동차_경주를_진행한다() { + void play_메서드는_자동차_경주를_진행한다() { // given final GameDao gameDao = Mockito.mock(GameDao.class); final CarDao carDao = Mockito.mock(CarDao.class); @@ -38,5 +39,4 @@ class RacingGameServiceTest { () -> assertThat(gameResponse.getRacingCars()).hasSize(3) ); } - } From 8f58fc38e563f75024d71a3da6c4046042781cfc Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 22:39:04 +0900 Subject: [PATCH 10/36] =?UTF-8?q?test:=20TestNumberGenerator=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=B2=B4=20deque=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/racingcar/utils/TestNumberGenerator.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/java/racingcar/utils/TestNumberGenerator.java b/src/test/java/racingcar/utils/TestNumberGenerator.java index 2ff72fe86..f299187c0 100644 --- a/src/test/java/racingcar/utils/TestNumberGenerator.java +++ b/src/test/java/racingcar/utils/TestNumberGenerator.java @@ -1,18 +1,20 @@ package racingcar.utils; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.List; import racingcar.domain.NumberGenerator; public class TestNumberGenerator implements NumberGenerator { - private final List numbers; + private final Deque numbers; public TestNumberGenerator(final List numbers) { - this.numbers = numbers; + this.numbers = new ArrayDeque<>(numbers); } @Override public int generate() { - return numbers.remove(0); + return numbers.removeFirst(); } } From 8c84be589bff85ae31a2b0d57d1fcf6b2f372a85 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 22:58:30 +0900 Subject: [PATCH 11/36] =?UTF-8?q?refactor:=20=EC=BD=98=EC=86=94=20?= =?UTF-8?q?=EC=95=A0=ED=94=8C=EB=A6=AC=EC=BC=80=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++- .../ConsoleRacingGameApplication.java | 31 +++++++++++ .../java/racingcar/RacingGameApplication.java | 39 ------------- .../ConsoleRacingGameController.java | 33 +++++++++++ .../controller/RacingGameController.java | 53 ------------------ .../java/racingcar/dao/ConsoleCarDao.java | 10 ++++ .../java/racingcar/dao/ConsoleGameDao.java | 10 ++++ src/main/java/racingcar/view/InputParser.java | 4 +- .../java/racingcar/view/InputValidator.java | 54 ------------------ src/main/java/racingcar/view/InputView.java | 25 ++------- src/main/java/racingcar/view/OutputView.java | 45 ++++++--------- .../java/racingcar/view/InputParserTest.java | 18 ++++-- .../racingcar/view/InputValidatorTest.java | 55 ------------------- 13 files changed, 127 insertions(+), 259 deletions(-) create mode 100644 src/main/java/racingcar/ConsoleRacingGameApplication.java delete mode 100644 src/main/java/racingcar/RacingGameApplication.java create mode 100644 src/main/java/racingcar/controller/ConsoleRacingGameController.java delete mode 100644 src/main/java/racingcar/controller/RacingGameController.java create mode 100644 src/main/java/racingcar/dao/ConsoleCarDao.java create mode 100644 src/main/java/racingcar/dao/ConsoleGameDao.java delete mode 100644 src/main/java/racingcar/view/InputValidator.java delete mode 100644 src/test/java/racingcar/view/InputValidatorTest.java diff --git a/README.md b/README.md index dab6af903..c6f5f8f9d 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,10 @@ - [x] 자동차 저장 기능 구현 - [ ] 게임 조회 기능 - [ ] 게임 조회 요청 구현 -- [ ] 콘솔 애플리케이션 출력 변경 - - [ ] 중간 과정을 출력하는 로직 제거 - - [ ] 우승자와 플레이어별 최종 이동거리를 출력하도록 수정 -- [ ] 콘솔과 웹의 중복 코드 제거 - - [ ] 중복 로직 제거 +- [x] 콘솔 애플리케이션 출력 변경 + - [x] 중간 과정을 출력하는 로직 제거 + - [x] 우승자와 플레이어별 최종 이동거리를 출력하도록 수정 +- [x] 콘솔과 웹의 중복 코드 제거 - [x] DB 연동하기 ### Database diff --git a/src/main/java/racingcar/ConsoleRacingGameApplication.java b/src/main/java/racingcar/ConsoleRacingGameApplication.java new file mode 100644 index 000000000..2390586cf --- /dev/null +++ b/src/main/java/racingcar/ConsoleRacingGameApplication.java @@ -0,0 +1,31 @@ +package racingcar; + +import java.util.Scanner; +import racingcar.controller.ConsoleRacingGameController; +import racingcar.dao.ConsoleCarDao; +import racingcar.dao.ConsoleGameDao; +import racingcar.domain.RandomNumberGenerator; +import racingcar.service.RacingGameService; +import racingcar.view.InputParser; +import racingcar.view.InputView; +import racingcar.view.OutputView; + +public class ConsoleRacingGameApplication { + public static void main(String[] args) { + final RacingGameService racingGameService = generateRacingGameService(); + final ConsoleRacingGameController racingGameController = new ConsoleRacingGameController( + racingGameService, + new InputView(new InputParser(), new Scanner(System.in)), + new OutputView() + ); + racingGameController.run(); + } + + private static RacingGameService generateRacingGameService() { + return new RacingGameService( + new RandomNumberGenerator(), + new ConsoleGameDao(), + new ConsoleCarDao() + ); + } +} diff --git a/src/main/java/racingcar/RacingGameApplication.java b/src/main/java/racingcar/RacingGameApplication.java deleted file mode 100644 index a100fe9d8..000000000 --- a/src/main/java/racingcar/RacingGameApplication.java +++ /dev/null @@ -1,39 +0,0 @@ -package racingcar; - -import java.util.Scanner; -import racingcar.controller.RacingGameController; -import racingcar.view.InputParser; -import racingcar.view.InputValidator; -import racingcar.view.InputView; -import racingcar.view.OutputView; - -public class RacingGameApplication { - - private static final Scanner scanner = new Scanner(System.in); - - public static void main(String[] args) { - RacingGameController racingGameController = new RacingGameController(inputView(), outputView()); - racingGameController.run(); - clean(); - } - - private static InputView inputView() { - return new InputView(inputValidator(), inputParser(), scanner); - } - - private static InputValidator inputValidator() { - return new InputValidator(); - } - - private static InputParser inputParser() { - return new InputParser(); - } - - private static OutputView outputView() { - return new OutputView(); - } - - private static void clean() { - scanner.close(); - } -} diff --git a/src/main/java/racingcar/controller/ConsoleRacingGameController.java b/src/main/java/racingcar/controller/ConsoleRacingGameController.java new file mode 100644 index 000000000..e075287ea --- /dev/null +++ b/src/main/java/racingcar/controller/ConsoleRacingGameController.java @@ -0,0 +1,33 @@ +package racingcar.controller; + +import racingcar.dto.GameRequestDto; +import racingcar.dto.GameResponseDto; +import racingcar.service.RacingGameService; +import racingcar.view.InputView; +import racingcar.view.OutputView; + +public class ConsoleRacingGameController { + private final RacingGameService racingGameService; + private final InputView inputView; + private final OutputView outputView; + + public ConsoleRacingGameController( + final RacingGameService racingGameService, + final InputView inputView, + final OutputView outputView + ) { + this.racingGameService = racingGameService; + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + try { + final GameRequestDto request = inputView.readGameRequest(); + final GameResponseDto response = racingGameService.play(request); + outputView.printResult(response); + } catch (final IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } +} diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameController.java deleted file mode 100644 index b1fd969f6..000000000 --- a/src/main/java/racingcar/controller/RacingGameController.java +++ /dev/null @@ -1,53 +0,0 @@ -package racingcar.controller; - -import java.util.List; -import java.util.function.Supplier; -import racingcar.domain.Car; -import racingcar.domain.RacingGame; -import racingcar.domain.RandomNumberGenerator; -import racingcar.view.InputView; -import racingcar.view.OutputView; - -public class RacingGameController { - - private final InputView inputView; - private final OutputView outputView; - - public RacingGameController(final InputView inputView, final OutputView outputView) { - this.inputView = inputView; - this.outputView = outputView; - } - - public T retry(final Supplier supplier) { - try { - return supplier.get(); - } catch (IllegalArgumentException e) { - outputView.printErrorMessage(e.getMessage()); - return retry(supplier); - } - } - - public void run() { - RacingGame racingGame = initialize(); - play(racingGame); - findWinners(racingGame); - } - - private RacingGame initialize() { - List carNames = retry(inputView::readCarNames); - int count = retry(inputView::readCount); - return new RacingGame(new RandomNumberGenerator(), carNames, count); - } - - private void play(final RacingGame racingGame) { - outputView.printResultMessage(); - racingGame.play(); - List cars = racingGame.getCars(); - outputView.printCurrentCarPositions(cars); - } - - private void findWinners(final RacingGame racingGame) { - List winners = racingGame.findWinners(); - outputView.printWinnersMessage(winners); - } -} diff --git a/src/main/java/racingcar/dao/ConsoleCarDao.java b/src/main/java/racingcar/dao/ConsoleCarDao.java new file mode 100644 index 000000000..8f2054140 --- /dev/null +++ b/src/main/java/racingcar/dao/ConsoleCarDao.java @@ -0,0 +1,10 @@ +package racingcar.dao; + +import java.util.List; +import racingcar.entity.CarEntity; + +public class ConsoleCarDao implements CarDao { + @Override + public void saveAll(final List players) { + } +} diff --git a/src/main/java/racingcar/dao/ConsoleGameDao.java b/src/main/java/racingcar/dao/ConsoleGameDao.java new file mode 100644 index 000000000..46bc87c5a --- /dev/null +++ b/src/main/java/racingcar/dao/ConsoleGameDao.java @@ -0,0 +1,10 @@ +package racingcar.dao; + +import racingcar.entity.GameEntity; + +public class ConsoleGameDao implements GameDao { + @Override + public int saveAndGetId(final GameEntity game) { + return 0; + } +} diff --git a/src/main/java/racingcar/view/InputParser.java b/src/main/java/racingcar/view/InputParser.java index e858d1ae2..6d84f2971 100644 --- a/src/main/java/racingcar/view/InputParser.java +++ b/src/main/java/racingcar/view/InputParser.java @@ -6,10 +6,8 @@ import java.util.List; public class InputParser { - private static final String DELIMITER = ","; private static final int SPLIT_LIMIT = -1; - private static final String INVALID_COUNT_MESSAGE = "시도할 회수는 정수만 입력할 수 있습니다. 입력한 값 : "; public List splitAndParseNames(final String input) { return Arrays.stream(input.split(DELIMITER, SPLIT_LIMIT)) @@ -21,7 +19,7 @@ public int parseInt(final String input) { try { return Integer.parseInt(input); } catch (NumberFormatException e) { - throw new IllegalArgumentException(INVALID_COUNT_MESSAGE + input); + throw new IllegalArgumentException("시도할 회수는 정수만 입력할 수 있습니다. 입력한 값 : " + input); } } } diff --git a/src/main/java/racingcar/view/InputValidator.java b/src/main/java/racingcar/view/InputValidator.java deleted file mode 100644 index defecc35b..000000000 --- a/src/main/java/racingcar/view/InputValidator.java +++ /dev/null @@ -1,54 +0,0 @@ -package racingcar.view; - -import static java.text.MessageFormat.format; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class InputValidator { - - private static final String INVALID_NAME_MESSAGE = "차의 이름은 {0}자 이상, {1}자 이하여야 합니다."; - private static final int NAME_LENGTH_LOWER_BOUND = 1; - private static final int NAME_LENGTH_UPPER_BOUND = 5; - private static final String DUPLICATED_NAMES_MESSAGE = "중복된 차 이름이 없어야 합니다."; - private static final String INVALID_COUNT_MESSAGE = "시도할 횟수는 {0}이상, {1}이하의 정수여야 합니다."; - private static final int COUNT_LOWER_BOUND = 1; - private static final int COUNT_UPPER_BOUND = 100; - - public void validateNames(final List names) { - names.forEach(this::validateNameLength); - validateDuplicateNames(names); - } - - private void validateNameLength(final String name) { - if (isInvalidNameLength(name)) { - throw new IllegalArgumentException( - format(INVALID_NAME_MESSAGE, NAME_LENGTH_LOWER_BOUND, NAME_LENGTH_UPPER_BOUND) - ); - } - } - - private boolean isInvalidNameLength(final String name) { - return name.length() < NAME_LENGTH_LOWER_BOUND || NAME_LENGTH_UPPER_BOUND < name.length(); - } - - private void validateDuplicateNames(final List names) { - Set nonDuplicatedNames = new HashSet<>(names); - if (names.size() != nonDuplicatedNames.size()) { - throw new IllegalArgumentException(DUPLICATED_NAMES_MESSAGE); - } - } - - public void validateCount(final int count) { - if (isInvalidCount(count)) { - throw new IllegalArgumentException( - format(INVALID_COUNT_MESSAGE, COUNT_LOWER_BOUND, COUNT_UPPER_BOUND) - ); - } - } - - private boolean isInvalidCount(final int count) { - return count < COUNT_LOWER_BOUND || COUNT_UPPER_BOUND < count; - } -} diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index 84ac80b94..762484d46 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -2,37 +2,24 @@ import java.util.List; import java.util.Scanner; +import racingcar.dto.GameRequestDto; public class InputView { - - private static final String READ_NAMES_MESSAGE = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; - private static final String READ_COUNT_MESSAGE = "시도할 회수는 몇회인가요?"; - - private final InputValidator inputValidator; private final InputParser inputParser; private final Scanner scanner; - public InputView(final InputValidator inputValidator, final InputParser inputParser, final Scanner scanner) { - this.inputValidator = inputValidator; + public InputView(final InputParser inputParser, final Scanner scanner) { this.inputParser = inputParser; this.scanner = scanner; } - public List readCarNames() { - System.out.println(READ_NAMES_MESSAGE); + public GameRequestDto readGameRequest() { + System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."); List names = inputParser.splitAndParseNames(scanner.nextLine()); - inputValidator.validateNames(names); - - return names; - } - - public int readCount() { - System.out.println(READ_COUNT_MESSAGE); + System.out.println("시도할 회수는 몇회인가요?"); int count = inputParser.parseInt(scanner.nextLine()); - inputValidator.validateCount(count); - - return count; + return new GameRequestDto(names, count); } } diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index d4300be11..e4d1cbb1b 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -1,49 +1,40 @@ package racingcar.view; -import static java.text.MessageFormat.format; - import java.util.List; import java.util.stream.Collectors; -import racingcar.domain.Car; +import racingcar.dto.CarDto; +import racingcar.dto.GameResponseDto; public class OutputView { + private static final String NEXT_LINE = System.lineSeparator(); + private static final String DELIMITER = ", "; - private static final String RESULT_MESSAGE = "\n실행 결과"; - private static final String POSITION_MESSAGE_FORMAT = "{0} : {1}"; - private static final String POSITION_SYMBOL = "-"; - private static final String POSITION_MESSAGE_DELIMITER = "\n"; - private static final String WINNERS_MESSAGE_FORMAT = "{0}가 최종 우승했습니다."; - private static final String WINNERS_MESSAGE_DELIMITER = ", "; - private static final String ERROR_MESSAGE = "[ERROR] "; - - public void printResultMessage() { - System.out.println(RESULT_MESSAGE); + public void printResult(final GameResponseDto gameResponseDto) { + System.out.println("실행 결과"); + printCurrentCarPositions(gameResponseDto.getRacingCars()); + printWinnersMessage(gameResponseDto.getWinners()); } - public void printCurrentCarPositions(final List cars) { - System.out.println(generatePositionMessages(cars) + POSITION_MESSAGE_DELIMITER); + private void printCurrentCarPositions(final List cars) { + System.out.println(generatePositionMessages(cars) + NEXT_LINE); } - private String generatePositionMessages(final List cars) { + private String generatePositionMessages(final List cars) { return cars.stream() .map(this::generatePositionMessage) - .collect(Collectors.joining(POSITION_MESSAGE_DELIMITER)); + .collect(Collectors.joining(NEXT_LINE)); } - private String generatePositionMessage(final Car car) { - return format( - POSITION_MESSAGE_FORMAT, - car.getName(), - POSITION_SYMBOL.repeat(car.getPosition()) - ); + private String generatePositionMessage(final CarDto car) { + return String.format("Name: %s : Position: %d", car.getName(), car.getPosition()); } - public void printWinnersMessage(final List winners) { - String winnersMessage = String.join(WINNERS_MESSAGE_DELIMITER, winners); - System.out.println(format(WINNERS_MESSAGE_FORMAT, winnersMessage)); + private void printWinnersMessage(final List winners) { + String winnersMessage = String.join(DELIMITER, winners); + System.out.println(String.format("%s가 최종 우승했습니다.", winnersMessage)); } public void printErrorMessage(final String message) { - System.out.println(ERROR_MESSAGE + message); + System.out.println("[ERROR] " + message); } } diff --git a/src/test/java/racingcar/view/InputParserTest.java b/src/test/java/racingcar/view/InputParserTest.java index 7189fd1a8..73fd1a0c7 100644 --- a/src/test/java/racingcar/view/InputParserTest.java +++ b/src/test/java/racingcar/view/InputParserTest.java @@ -5,14 +5,19 @@ import java.util.List; import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +@DisplayName("InputParser 클래스") +@DisplayNameGeneration(ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") class InputParserTest { - private final InputParser inputParser = new InputParser(); static Stream nameInputAndSeparatedNames() { @@ -27,15 +32,18 @@ static Stream nameInputAndSeparatedNames() { @ParameterizedTest(name = "splitAndParseNames 메서드는 쉼표(,) 기준으로 문자열을 분리하여 반환한다. 입력값: \"{0}\" 결과: {1}") @MethodSource("nameInputAndSeparatedNames") - void should_returnSeparatedNames_when_splitAndParseNames(final String input, final List separatedNames) { + void splitAndParseNames_메서드는_쉼표_기준으로_문자열을_분리하여_반환한다(final String input, final List separatedNames) { + // given List result = inputParser.splitAndParseNames(input); + // expect assertThat(result).containsAll(separatedNames); } @ParameterizedTest(name = "parseInt 메서드는 Integer 범위의 정수가 아닌 문자열을 입력하는 경우 예외를 던진다. 입력값: \"{0}\"") @ValueSource(strings = {"", " ", "!", "a", "가", "A", "1.2", "3000000000"}) - void should_throwException_when_inputIsNotIntegerString(final String input) { + void parseInt_메서드는_Integer_범위의_정수가_아닌_문자열을_입력하는_경우_예외를_던진다(final String input) { + // expect assertThatThrownBy(() -> inputParser.parseInt(input)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("시도할 회수는 정수만 입력할 수 있습니다. 입력한 값 : " + input); @@ -43,9 +51,11 @@ void should_throwException_when_inputIsNotIntegerString(final String input) { @ParameterizedTest(name = "parseInt 메서드는 정수 문자열을 입력하는 경우 int 값으로 반환한다. 입력값: \"{0}\"") @CsvSource({"-1,-1", "0,0", "1,1"}) - void should_returnIntValue_when_inputIsIntegerString(final String input, final int result) { + void parseInt_메서드는_정수_문자열을_입력하는_경우_int_값으로_반환한다(final String input, final int result) { + // given int parsedValue = inputParser.parseInt(input); + // expect assertThat(parsedValue).isEqualTo(result); } } diff --git a/src/test/java/racingcar/view/InputValidatorTest.java b/src/test/java/racingcar/view/InputValidatorTest.java deleted file mode 100644 index 5217a975b..000000000 --- a/src/test/java/racingcar/view/InputValidatorTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package racingcar.view; - -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import java.util.List; -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; - -class InputValidatorTest { - - private final InputValidator inputValidator = new InputValidator(); - - @ParameterizedTest(name = "validateNames 메서드는 허용되지 않는 길이의 이름[{0}]을 입력받는 경우 예외를 던진다.") - @ValueSource(strings = {"", "Dazzle"}) - void should_throwException_when_invalidNames(final String name) { - assertThatThrownBy(() -> inputValidator.validateNames(List.of(name))) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("차의 이름은 1자 이상, 5자 이하여야 합니다."); - } - - @Test - @DisplayName("validateNames 메서드는 올바른 길이의 차 이름 목록을 입력받는 경우 예외를 던지지 않는다.") - void should_noException_when_validNames() { - List names = List.of("차", "다즐", "라틴어수업"); - - assertThatNoException().isThrownBy(() -> inputValidator.validateNames(names)); - } - - @Test - @DisplayName("validateNames 메서드는 중복된 이름이 포함된 이름 목록을 받는 경우 예외를 던진다.") - void should_throwException_when_duplicatedNames() { - final List duplicatedNames = List.of("다즐", "허브", "허브"); - - assertThatThrownBy(() -> inputValidator.validateNames(duplicatedNames)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("중복된 차 이름이 없어야 합니다."); - } - - @ParameterizedTest(name = "validateCount 메서드는 1 ~ 100 사이의 정수가 아닌 값을 입력받는 경우 예외를 던진다. 입력값: {0}") - @ValueSource(ints = {0, -1, 101}) - void should_throwException_when_invalidCount(final int count) { - assertThatThrownBy(() -> inputValidator.validateCount(count)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("시도할 횟수는 1이상, 100이하의 정수여야 합니다."); - } - - @ParameterizedTest(name = "validateCount 메서드는 1 ~ 100 사이의 정수를 입력받는 경우 예외를 던지지 않는다. 입력값: {0}") - @ValueSource(ints = {1, 50, 100}) - void should_noException_when_validCount(final int count) { - assertThatNoException().isThrownBy(() -> inputValidator.validateCount(count)); - } -} From e6dfb733355c32f6a10cbaea074a92ebc2b0566d Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 23:23:14 +0900 Subject: [PATCH 12/36] =?UTF-8?q?refactor:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=EC=8B=9C=20SimpleJdbcInsert=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/main/java/racingcar/dao/CarJdbcDao.java | 35 ++++++------------- src/main/java/racingcar/dao/GameJdbcDao.java | 26 +++++--------- src/main/resources/data.sql | 2 +- .../java/racingcar/dao/CarJdbcDaoTest.java | 2 +- 5 files changed, 23 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index c6f5f8f9d..40f80a11a 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ### Database -image +image ### API diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index 09a463405..f757b2890 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -1,39 +1,26 @@ package racingcar.dao; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.util.List; -import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Component; import racingcar.entity.CarEntity; @Component public class CarJdbcDao implements CarDao { - private final JdbcTemplate jdbcTemplate; + private final SimpleJdbcInsert simpleJdbcInsert; public CarJdbcDao(final JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; + this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("Car") + .usingGeneratedKeyColumns("id"); } - public void saveAll(final List players) { - final String sql = "INSERT INTO player(name, position, winner, game_id) VALUES (?, ?, ?, ?)"; - final BatchPreparedStatementSetter batchPreparedStatementSetter = new BatchPreparedStatementSetter() { - - @Override - public void setValues(PreparedStatement ps, int i) throws SQLException { - final CarEntity player = players.get(i); - ps.setString(1, player.getName()); - ps.setInt(2, player.getPosition()); - ps.setBoolean(3, player.isWinner()); - ps.setInt(4, player.getGameId()); - } - - @Override - public int getBatchSize() { - return players.size(); - } - }; - jdbcTemplate.batchUpdate(sql, batchPreparedStatementSetter); + public void saveAll(final List cars) { + final BeanPropertySqlParameterSource[] parameterSources = cars.stream() + .map(BeanPropertySqlParameterSource::new) + .toArray(BeanPropertySqlParameterSource[]::new); + simpleJdbcInsert.executeBatch(parameterSources); } } diff --git a/src/main/java/racingcar/dao/GameJdbcDao.java b/src/main/java/racingcar/dao/GameJdbcDao.java index e07254adf..c05a68bfa 100644 --- a/src/main/java/racingcar/dao/GameJdbcDao.java +++ b/src/main/java/racingcar/dao/GameJdbcDao.java @@ -1,33 +1,25 @@ package racingcar.dao; -import java.sql.PreparedStatement; -import java.sql.Timestamp; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; +import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Component; import racingcar.entity.GameEntity; @Component public class GameJdbcDao implements GameDao { - private final JdbcTemplate jdbcTemplate; + private final SimpleJdbcInsert jdbcInsert; public GameJdbcDao(final JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; + this.jdbcInsert = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("Game") + .usingGeneratedKeyColumns("id"); } @Override public int saveAndGetId(final GameEntity game) { - final String sql = "insert into game (trial, created_at) values (?, ?)"; - - final KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"}); - ps.setInt(1, game.getTrial()); - ps.setTimestamp(2, Timestamp.valueOf(game.getCreatedAt())); - return ps; - }, keyHolder); - - return keyHolder.getKey().intValue(); + SqlParameterSource params = new BeanPropertySqlParameterSource(game); + return jdbcInsert.executeAndReturnKey(params).intValue(); } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 21e35c5ae..50cc7054a 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -7,7 +7,7 @@ CREATE TABLE GAME PRIMARY KEY (id) ); -CREATE TABLE PLAYER +CREATE TABLE CAR ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(255) NOT NULL, diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 2e448c8b8..77cc15752 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -42,7 +42,7 @@ void setUp() { carDao.saveAll(players); // then - final String sql = "select count(*) from player"; + final String sql = "select count(*) from car"; final int count = jdbcTemplate.queryForObject(sql, Integer.class); assertThat(count).isEqualTo(1); } From afe536ad3b0bea64b91b4c3f82079202dc9bba41 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 23:30:08 +0900 Subject: [PATCH 13/36] =?UTF-8?q?refactor:=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EC=83=81=EC=88=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/domain/Car.java | 1 - src/main/java/racingcar/domain/Cars.java | 8 ++------ src/main/java/racingcar/domain/Count.java | 1 - src/main/java/racingcar/domain/Name.java | 8 ++++---- src/main/java/racingcar/domain/Position.java | 9 ++++----- src/main/java/racingcar/domain/RacingGame.java | 1 - .../java/racingcar/domain/RandomNumberGenerator.java | 1 - 7 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java index 1cbfe1899..2581a1119 100644 --- a/src/main/java/racingcar/domain/Car.java +++ b/src/main/java/racingcar/domain/Car.java @@ -1,7 +1,6 @@ package racingcar.domain; public class Car implements Comparable { - private static final int MOVE_LOWER_BOUND = 4; private final Name name; diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java index cd217194c..516ab66ed 100644 --- a/src/main/java/racingcar/domain/Cars.java +++ b/src/main/java/racingcar/domain/Cars.java @@ -9,10 +9,6 @@ import java.util.Set; public class Cars { - - private static final String INVALID_WINNER_MESSAGE = "우승자가 존재하지 않습니다."; - private static final String DUPLICATED_NAMES_MESSAGE = "중복된 차 이름이 없어야 합니다."; - private final List cars; public Cars(final List names) { @@ -23,7 +19,7 @@ public Cars(final List names) { private void validate(final List names) { Set nonDuplicateNames = new HashSet<>(names); if (names.size() != nonDuplicateNames.size()) { - throw new IllegalArgumentException(DUPLICATED_NAMES_MESSAGE); + throw new IllegalArgumentException("중복된 차 이름이 없어야 합니다."); } } @@ -47,7 +43,7 @@ public List findWinners() { private Car findWinner() { return cars.stream() .max(Car::compareTo) - .orElseThrow(() -> new IllegalArgumentException(INVALID_WINNER_MESSAGE)); + .orElseThrow(() -> new IllegalArgumentException("우승자가 존재하지 않습니다.")); } private List findWinners(final Car winner) { diff --git a/src/main/java/racingcar/domain/Count.java b/src/main/java/racingcar/domain/Count.java index 1ccf45d20..09233c2c0 100644 --- a/src/main/java/racingcar/domain/Count.java +++ b/src/main/java/racingcar/domain/Count.java @@ -1,7 +1,6 @@ package racingcar.domain; public class Count { - private static final int PLAYABLE_LOWER_BOUND = 1; private int value; diff --git a/src/main/java/racingcar/domain/Name.java b/src/main/java/racingcar/domain/Name.java index 21aa94123..9bdc9422a 100644 --- a/src/main/java/racingcar/domain/Name.java +++ b/src/main/java/racingcar/domain/Name.java @@ -1,11 +1,8 @@ package racingcar.domain; public class Name { - private static final int NAME_LOWER_BOUND = 1; private static final int NAME_UPPER_BOUND = 5; - private static final String INVALID_NAME_LENGTH_MESSAGE = - "차의 이름은 " + NAME_LOWER_BOUND + "자 이상, " + NAME_UPPER_BOUND + "자 이하여야 합니다. 입력된 차 이름 : "; private final String value; @@ -16,7 +13,10 @@ public class Name { private void validate(final String name) { if (isInvalidNameLength(name)) { - throw new IllegalArgumentException(INVALID_NAME_LENGTH_MESSAGE + name); + throw new IllegalArgumentException( + "차의 이름은 " + NAME_LOWER_BOUND + "자 이상, " + NAME_UPPER_BOUND + "자 이하여야 합니다." + + " 입력된 차 이름 : " + name + ); } } diff --git a/src/main/java/racingcar/domain/Position.java b/src/main/java/racingcar/domain/Position.java index 21ad3ff9f..af3f75452 100644 --- a/src/main/java/racingcar/domain/Position.java +++ b/src/main/java/racingcar/domain/Position.java @@ -3,7 +3,6 @@ import java.util.Objects; public class Position implements Comparable { - private static final int INITIAL_VALUE = 0; private int value; @@ -28,6 +27,10 @@ public boolean equals(final Object o) { return getValue() == position.getValue(); } + public int getValue() { + return value; + } + @Override public int hashCode() { return Objects.hash(getValue()); @@ -37,8 +40,4 @@ public int hashCode() { public int compareTo(final Position other) { return this.value - other.value; } - - public int getValue() { - return value; - } } diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java index c135b6b1d..cc3946403 100644 --- a/src/main/java/racingcar/domain/RacingGame.java +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -3,7 +3,6 @@ import java.util.List; public class RacingGame { - private final NumberGenerator numberGenerator; private final Cars cars; private final Count count; diff --git a/src/main/java/racingcar/domain/RandomNumberGenerator.java b/src/main/java/racingcar/domain/RandomNumberGenerator.java index 87a5103c0..9ee48b53b 100644 --- a/src/main/java/racingcar/domain/RandomNumberGenerator.java +++ b/src/main/java/racingcar/domain/RandomNumberGenerator.java @@ -3,7 +3,6 @@ import java.util.Random; public class RandomNumberGenerator implements NumberGenerator { - private static final int NUMBER_UPPER_BOUND = 10; private final Random random = new Random(); From 8774a6c3c460dbf81818844f4741e3802dbec534 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 17 Apr 2023 23:32:07 +0900 Subject: [PATCH 14/36] =?UTF-8?q?refactor:=20=EB=8D=94=EC=9A=B1=20?= =?UTF-8?q?=EC=9D=98=EB=AF=B8=EC=9E=88=EB=8A=94=20DTO=EB=AA=85=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ConsoleRacingGameController.java | 8 ++++---- .../controller/WebRacingGameController.java | 8 ++++---- ...{GameRequestDto.java => GamePlayRequestDto.java} | 4 ++-- ...ameResponseDto.java => GamePlayResponseDto.java} | 4 ++-- .../java/racingcar/service/RacingGameService.java | 12 ++++++------ src/main/java/racingcar/view/InputView.java | 6 +++--- src/main/java/racingcar/view/OutputView.java | 13 +++++++------ .../controller/WebRacingGameControllerTest.java | 10 +++++----- .../racingcar/service/RacingGameServiceTest.java | 8 ++++---- 9 files changed, 37 insertions(+), 36 deletions(-) rename src/main/java/racingcar/dto/{GameRequestDto.java => GamePlayRequestDto.java} (73%) rename src/main/java/racingcar/dto/{GameResponseDto.java => GamePlayResponseDto.java} (73%) diff --git a/src/main/java/racingcar/controller/ConsoleRacingGameController.java b/src/main/java/racingcar/controller/ConsoleRacingGameController.java index e075287ea..8db0b50d6 100644 --- a/src/main/java/racingcar/controller/ConsoleRacingGameController.java +++ b/src/main/java/racingcar/controller/ConsoleRacingGameController.java @@ -1,7 +1,7 @@ package racingcar.controller; -import racingcar.dto.GameRequestDto; -import racingcar.dto.GameResponseDto; +import racingcar.dto.GamePlayRequestDto; +import racingcar.dto.GamePlayResponseDto; import racingcar.service.RacingGameService; import racingcar.view.InputView; import racingcar.view.OutputView; @@ -23,8 +23,8 @@ public ConsoleRacingGameController( public void run() { try { - final GameRequestDto request = inputView.readGameRequest(); - final GameResponseDto response = racingGameService.play(request); + final GamePlayRequestDto request = inputView.readGamePlayRequest(); + final GamePlayResponseDto response = racingGameService.play(request); outputView.printResult(response); } catch (final IllegalArgumentException e) { outputView.printErrorMessage(e.getMessage()); diff --git a/src/main/java/racingcar/controller/WebRacingGameController.java b/src/main/java/racingcar/controller/WebRacingGameController.java index 27d9429a4..b3027f86f 100644 --- a/src/main/java/racingcar/controller/WebRacingGameController.java +++ b/src/main/java/racingcar/controller/WebRacingGameController.java @@ -3,8 +3,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import racingcar.dto.GameRequestDto; -import racingcar.dto.GameResponseDto; +import racingcar.dto.GamePlayRequestDto; +import racingcar.dto.GamePlayResponseDto; import racingcar.service.RacingGameService; @RestController @@ -16,7 +16,7 @@ public WebRacingGameController(final RacingGameService racingGameService) { } @PostMapping("/plays") - public GameResponseDto plays(@RequestBody final GameRequestDto gameRequest) { - return racingGameService.play(gameRequest); + public GamePlayResponseDto plays(@RequestBody final GamePlayRequestDto request) { + return racingGameService.play(request); } } diff --git a/src/main/java/racingcar/dto/GameRequestDto.java b/src/main/java/racingcar/dto/GamePlayRequestDto.java similarity index 73% rename from src/main/java/racingcar/dto/GameRequestDto.java rename to src/main/java/racingcar/dto/GamePlayRequestDto.java index ff9ec0466..8a89aa232 100644 --- a/src/main/java/racingcar/dto/GameRequestDto.java +++ b/src/main/java/racingcar/dto/GamePlayRequestDto.java @@ -2,11 +2,11 @@ import java.util.List; -public class GameRequestDto { +public class GamePlayRequestDto { private final List names; private final int count; - public GameRequestDto(final List names, final int count) { + public GamePlayRequestDto(final List names, final int count) { this.names = names; this.count = count; } diff --git a/src/main/java/racingcar/dto/GameResponseDto.java b/src/main/java/racingcar/dto/GamePlayResponseDto.java similarity index 73% rename from src/main/java/racingcar/dto/GameResponseDto.java rename to src/main/java/racingcar/dto/GamePlayResponseDto.java index 389c330a6..6ced1d857 100644 --- a/src/main/java/racingcar/dto/GameResponseDto.java +++ b/src/main/java/racingcar/dto/GamePlayResponseDto.java @@ -2,11 +2,11 @@ import java.util.List; -public class GameResponseDto { +public class GamePlayResponseDto { private final List winners; private final List racingCars; - public GameResponseDto(final List winners, final List racingCars) { + public GamePlayResponseDto(final List winners, final List racingCars) { this.winners = winners; this.racingCars = racingCars; } diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index e332589f4..fee4603c9 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -12,8 +12,8 @@ import racingcar.domain.NumberGenerator; import racingcar.domain.RacingGame; import racingcar.dto.CarDto; -import racingcar.dto.GameRequestDto; -import racingcar.dto.GameResponseDto; +import racingcar.dto.GamePlayRequestDto; +import racingcar.dto.GamePlayResponseDto; import racingcar.entity.CarEntity; import racingcar.entity.GameEntity; @@ -29,7 +29,7 @@ public RacingGameService(final NumberGenerator numberGenerator, final GameDao ga this.carDao = carDao; } - public GameResponseDto play(final GameRequestDto gameRequest) { + public GamePlayResponseDto play(final GamePlayRequestDto gameRequest) { final RacingGame racingGame = playRacingGame(gameRequest); final GameEntity game = new GameEntity(gameRequest.getCount()); @@ -45,16 +45,16 @@ public GameResponseDto play(final GameRequestDto gameRequest) { return toGameResponse(racingGame.findWinners(), cars); } - private RacingGame playRacingGame(final GameRequestDto gameRequest) { + private RacingGame playRacingGame(final GamePlayRequestDto gameRequest) { final RacingGame racingGame = new RacingGame(numberGenerator, gameRequest.getNames(), gameRequest.getCount()); racingGame.play(); return racingGame; } - private GameResponseDto toGameResponse(final List winners, final List cars) { + private GamePlayResponseDto toGameResponse(final List winners, final List cars) { final List carDtos = cars.stream() .map(car -> new CarDto(car.getName(), car.getPosition())) .collect(toList()); - return new GameResponseDto(winners, carDtos); + return new GamePlayResponseDto(winners, carDtos); } } diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index 762484d46..0c55c3f39 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -2,7 +2,7 @@ import java.util.List; import java.util.Scanner; -import racingcar.dto.GameRequestDto; +import racingcar.dto.GamePlayRequestDto; public class InputView { private final InputParser inputParser; @@ -13,13 +13,13 @@ public InputView(final InputParser inputParser, final Scanner scanner) { this.scanner = scanner; } - public GameRequestDto readGameRequest() { + public GamePlayRequestDto readGamePlayRequest() { System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."); List names = inputParser.splitAndParseNames(scanner.nextLine()); System.out.println("시도할 회수는 몇회인가요?"); int count = inputParser.parseInt(scanner.nextLine()); - return new GameRequestDto(names, count); + return new GamePlayRequestDto(names, count); } } diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index e4d1cbb1b..41e49b335 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -1,18 +1,19 @@ package racingcar.view; +import static java.util.stream.Collectors.joining; + import java.util.List; -import java.util.stream.Collectors; import racingcar.dto.CarDto; -import racingcar.dto.GameResponseDto; +import racingcar.dto.GamePlayResponseDto; public class OutputView { private static final String NEXT_LINE = System.lineSeparator(); private static final String DELIMITER = ", "; - public void printResult(final GameResponseDto gameResponseDto) { + public void printResult(final GamePlayResponseDto response) { System.out.println("실행 결과"); - printCurrentCarPositions(gameResponseDto.getRacingCars()); - printWinnersMessage(gameResponseDto.getWinners()); + printCurrentCarPositions(response.getRacingCars()); + printWinnersMessage(response.getWinners()); } private void printCurrentCarPositions(final List cars) { @@ -22,7 +23,7 @@ private void printCurrentCarPositions(final List cars) { private String generatePositionMessages(final List cars) { return cars.stream() .map(this::generatePositionMessage) - .collect(Collectors.joining(NEXT_LINE)); + .collect(joining(NEXT_LINE)); } private String generatePositionMessage(final CarDto car) { diff --git a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java index be59a7721..3bb94c179 100644 --- a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java @@ -21,8 +21,8 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; import racingcar.dto.CarDto; -import racingcar.dto.GameRequestDto; -import racingcar.dto.GameResponseDto; +import racingcar.dto.GamePlayRequestDto; +import racingcar.dto.GamePlayResponseDto; import racingcar.service.RacingGameService; @WebMvcTest @@ -43,13 +43,13 @@ class WebRacingGameControllerTest { @Test void play_메서드는_게임을_진행한다() throws Exception { // given - final GameRequestDto gameRequest = new GameRequestDto(List.of("비버", "허브"), 1); + final GamePlayRequestDto gameRequest = new GamePlayRequestDto(List.of("비버", "허브"), 1); final String request = objectMapper.writeValueAsString(gameRequest); - final GameResponseDto gameResponse = new GameResponseDto( + final GamePlayResponseDto gameResponse = new GamePlayResponseDto( List.of("비버"), List.of(new CarDto("비버", 1), new CarDto("허브", 0)) ); - given(racingGameService.play(any(GameRequestDto.class))) + given(racingGameService.play(any(GamePlayRequestDto.class))) .willReturn(gameResponse); // expect diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 3e56024ec..ebeecded6 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -13,8 +13,8 @@ import racingcar.dao.CarDao; import racingcar.dao.GameDao; import racingcar.domain.NumberGenerator; -import racingcar.dto.GameRequestDto; -import racingcar.dto.GameResponseDto; +import racingcar.dto.GamePlayRequestDto; +import racingcar.dto.GamePlayResponseDto; import racingcar.utils.TestNumberGenerator; @DisplayName("RacingGameService 클래스") @@ -28,10 +28,10 @@ class RacingGameServiceTest { final CarDao carDao = Mockito.mock(CarDao.class); final NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 3)); final RacingGameService racingGameService = new RacingGameService(numberGenerator, gameDao, carDao); - final GameRequestDto gameRequest = new GameRequestDto(List.of("브리", "비버", "허브"), 1); + final GamePlayRequestDto gameRequest = new GamePlayRequestDto(List.of("브리", "비버", "허브"), 1); // when - final GameResponseDto gameResponse = racingGameService.play(gameRequest); + final GamePlayResponseDto gameResponse = racingGameService.play(gameRequest); // then assertAll( From a1331400c7ce084f96deae18e68a12a2054ab303 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 00:29:53 +0900 Subject: [PATCH 15/36] =?UTF-8?q?feat:=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EB=A5=BC=20=EB=A7=A4=ED=95=91=ED=95=B4=EC=A3=BC=EB=8A=94=20?= =?UTF-8?q?=EC=97=AD=ED=95=A0=EC=9D=84=20=EB=8B=B4=EB=8B=B9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ConsoleRacingGameApplication.java | 2 + .../racingcar/dto/GamePlayResponseDto.java | 10 ++++ .../racingcar/service/RacingGameMapper.java | 26 +++++++++ .../racingcar/service/RacingGameService.java | 47 ++++++---------- .../service/RacingGameMapperTest.java | 53 +++++++++++++++++++ .../service/RacingGameServiceTest.java | 8 ++- 6 files changed, 115 insertions(+), 31 deletions(-) create mode 100644 src/main/java/racingcar/service/RacingGameMapper.java create mode 100644 src/test/java/racingcar/service/RacingGameMapperTest.java diff --git a/src/main/java/racingcar/ConsoleRacingGameApplication.java b/src/main/java/racingcar/ConsoleRacingGameApplication.java index 2390586cf..88ff9766b 100644 --- a/src/main/java/racingcar/ConsoleRacingGameApplication.java +++ b/src/main/java/racingcar/ConsoleRacingGameApplication.java @@ -5,6 +5,7 @@ import racingcar.dao.ConsoleCarDao; import racingcar.dao.ConsoleGameDao; import racingcar.domain.RandomNumberGenerator; +import racingcar.service.RacingGameMapper; import racingcar.service.RacingGameService; import racingcar.view.InputParser; import racingcar.view.InputView; @@ -24,6 +25,7 @@ public static void main(String[] args) { private static RacingGameService generateRacingGameService() { return new RacingGameService( new RandomNumberGenerator(), + new RacingGameMapper(), new ConsoleGameDao(), new ConsoleCarDao() ); diff --git a/src/main/java/racingcar/dto/GamePlayResponseDto.java b/src/main/java/racingcar/dto/GamePlayResponseDto.java index 6ced1d857..6bf78b790 100644 --- a/src/main/java/racingcar/dto/GamePlayResponseDto.java +++ b/src/main/java/racingcar/dto/GamePlayResponseDto.java @@ -1,6 +1,9 @@ package racingcar.dto; +import static java.util.stream.Collectors.toList; + import java.util.List; +import racingcar.domain.Car; public class GamePlayResponseDto { private final List winners; @@ -11,6 +14,13 @@ public GamePlayResponseDto(final List winners, final List racing this.racingCars = racingCars; } + public static GamePlayResponseDto of(final List winners, final List cars) { + final List carDtos = cars.stream() + .map(car -> new CarDto(car.getName(), car.getPosition())) + .collect(toList()); + return new GamePlayResponseDto(winners, carDtos); + } + public List getWinners() { return winners; } diff --git a/src/main/java/racingcar/service/RacingGameMapper.java b/src/main/java/racingcar/service/RacingGameMapper.java new file mode 100644 index 000000000..13c2f9c05 --- /dev/null +++ b/src/main/java/racingcar/service/RacingGameMapper.java @@ -0,0 +1,26 @@ +package racingcar.service; + +import static java.util.stream.Collectors.toList; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.stereotype.Component; +import racingcar.domain.RacingGame; +import racingcar.entity.CarEntity; +import racingcar.entity.GameEntity; + +@Component +public class RacingGameMapper { + + public GameEntity toGameEntity(final int trial) { + return new GameEntity(trial); + } + + public List toCarEntities(final RacingGame racingGame, final int gameId) { + final Set winners = new HashSet<>(racingGame.findWinners()); + return racingGame.getCars().stream() + .map(car -> CarEntity.of(car, winners.contains(car.getName()), gameId)) + .collect(toList()); + } +} diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index fee4603c9..2f1a95670 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -1,17 +1,12 @@ package racingcar.service; -import static java.util.stream.Collectors.toList; - -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import racingcar.dao.CarDao; import racingcar.dao.GameDao; -import racingcar.domain.Car; import racingcar.domain.NumberGenerator; import racingcar.domain.RacingGame; -import racingcar.dto.CarDto; import racingcar.dto.GamePlayRequestDto; import racingcar.dto.GamePlayResponseDto; import racingcar.entity.CarEntity; @@ -20,41 +15,33 @@ @Service public class RacingGameService { private final NumberGenerator numberGenerator; + private final RacingGameMapper racingGameMapper; private final GameDao gameDao; private final CarDao carDao; - public RacingGameService(final NumberGenerator numberGenerator, final GameDao gameDao, final CarDao carDao) { + public RacingGameService( + final NumberGenerator numberGenerator, + final RacingGameMapper racingGameMapper, + final GameDao gameDao, + final CarDao carDao + ) { this.numberGenerator = numberGenerator; + this.racingGameMapper = racingGameMapper; this.gameDao = gameDao; this.carDao = carDao; } + @Transactional public GamePlayResponseDto play(final GamePlayRequestDto gameRequest) { - final RacingGame racingGame = playRacingGame(gameRequest); - - final GameEntity game = new GameEntity(gameRequest.getCount()); - final int gameId = gameDao.saveAndGetId(game); + final RacingGame game = new RacingGame(numberGenerator, gameRequest.getNames(), gameRequest.getCount()); + game.play(); - final List cars = racingGame.getCars(); - final Set winners = new HashSet<>(racingGame.findWinners()); - final List players = cars.stream() - .map(car -> CarEntity.of(car, winners.contains(car.getName()), gameId)) - .collect(toList()); - carDao.saveAll(players); + final GameEntity gameEntity = racingGameMapper.toGameEntity(gameRequest.getCount()); + final int gameId = gameDao.saveAndGetId(gameEntity); - return toGameResponse(racingGame.findWinners(), cars); - } - - private RacingGame playRacingGame(final GamePlayRequestDto gameRequest) { - final RacingGame racingGame = new RacingGame(numberGenerator, gameRequest.getNames(), gameRequest.getCount()); - racingGame.play(); - return racingGame; - } + final List carEntities = racingGameMapper.toCarEntities(game, gameId); + carDao.saveAll(carEntities); - private GamePlayResponseDto toGameResponse(final List winners, final List cars) { - final List carDtos = cars.stream() - .map(car -> new CarDto(car.getName(), car.getPosition())) - .collect(toList()); - return new GamePlayResponseDto(winners, carDtos); + return GamePlayResponseDto.of(game.findWinners(), game.getCars()); } } diff --git a/src/test/java/racingcar/service/RacingGameMapperTest.java b/src/test/java/racingcar/service/RacingGameMapperTest.java new file mode 100644 index 000000000..a0fdbe3a3 --- /dev/null +++ b/src/test/java/racingcar/service/RacingGameMapperTest.java @@ -0,0 +1,53 @@ +package racingcar.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import racingcar.domain.RacingGame; +import racingcar.entity.CarEntity; +import racingcar.entity.GameEntity; +import racingcar.utils.TestNumberGenerator; + +@DisplayName("RacingGameMapper 클래스") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +public class RacingGameMapperTest { + + private final RacingGameMapper racingGameMapper = new RacingGameMapper(); + + @Test + void toGameEntity_메서드는_시도_횟수를_입력받아_게임_엔티티를_반환한다() { + // given + final int trial = 5; + + // when + final GameEntity gameEntity = racingGameMapper.toGameEntity(trial); + + // then + assertThat(gameEntity.getTrial()).isEqualTo(5); + } + + @Test + void toCarEntities_메서드는_RacingGame과_gameId를_받아_자동차_엔티티들을_반환한다() { + // given + final TestNumberGenerator numberGenerator = new TestNumberGenerator(List.of(4, 3)); + final RacingGame racingGame = new RacingGame(numberGenerator, List.of("비버", "허브"), 1); + racingGame.play(); + final int gameId = 1; + + // when + final List carEntities = racingGameMapper.toCarEntities(racingGame, gameId); + + // then + assertAll( + () -> assertThat(carEntities).hasSize(2), + () -> assertThat(carEntities.get(0).getName()).isEqualTo("비버"), + () -> assertThat(carEntities.get(0).getPosition()).isEqualTo(1) + ); + } +} diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index ebeecded6..9bfd2c414 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -27,7 +27,13 @@ class RacingGameServiceTest { final GameDao gameDao = Mockito.mock(GameDao.class); final CarDao carDao = Mockito.mock(CarDao.class); final NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 3)); - final RacingGameService racingGameService = new RacingGameService(numberGenerator, gameDao, carDao); + final RacingGameMapper racingGameMapper = new RacingGameMapper(); + final RacingGameService racingGameService = new RacingGameService( + numberGenerator, + racingGameMapper, + gameDao, + carDao + ); final GamePlayRequestDto gameRequest = new GamePlayRequestDto(List.of("브리", "비버", "허브"), 1); // when From 9dc8c730190e933277b11f41ecda79c9c4994191 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 00:38:15 +0900 Subject: [PATCH 16/36] =?UTF-8?q?feat:=20GameDao=EA=B0=80=20=ED=82=A4?= =?UTF-8?q?=EB=A5=BC=20Optional=20=ED=98=95=ED=83=9C=EB=A1=9C=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/ConsoleGameDao.java | 5 +++-- src/main/java/racingcar/dao/GameDao.java | 3 ++- src/main/java/racingcar/dao/GameJdbcDao.java | 5 +++-- src/main/java/racingcar/service/RacingGameService.java | 3 ++- src/test/java/racingcar/dao/CarJdbcDaoTest.java | 2 +- src/test/java/racingcar/dao/GameJdbcDaoTest.java | 5 +++-- src/test/java/racingcar/service/RacingGameServiceTest.java | 4 ++++ 7 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/racingcar/dao/ConsoleGameDao.java b/src/main/java/racingcar/dao/ConsoleGameDao.java index 46bc87c5a..bd962379b 100644 --- a/src/main/java/racingcar/dao/ConsoleGameDao.java +++ b/src/main/java/racingcar/dao/ConsoleGameDao.java @@ -1,10 +1,11 @@ package racingcar.dao; +import java.util.Optional; import racingcar.entity.GameEntity; public class ConsoleGameDao implements GameDao { @Override - public int saveAndGetId(final GameEntity game) { - return 0; + public Optional saveAndGetId(final GameEntity game) { + return Optional.of(0); } } diff --git a/src/main/java/racingcar/dao/GameDao.java b/src/main/java/racingcar/dao/GameDao.java index c3b0de16d..82fadef54 100644 --- a/src/main/java/racingcar/dao/GameDao.java +++ b/src/main/java/racingcar/dao/GameDao.java @@ -1,7 +1,8 @@ package racingcar.dao; +import java.util.Optional; import racingcar.entity.GameEntity; public interface GameDao { - int saveAndGetId(final GameEntity game); + Optional saveAndGetId(final GameEntity game); } diff --git a/src/main/java/racingcar/dao/GameJdbcDao.java b/src/main/java/racingcar/dao/GameJdbcDao.java index c05a68bfa..79e57004f 100644 --- a/src/main/java/racingcar/dao/GameJdbcDao.java +++ b/src/main/java/racingcar/dao/GameJdbcDao.java @@ -1,5 +1,6 @@ package racingcar.dao; +import java.util.Optional; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; @@ -18,8 +19,8 @@ public GameJdbcDao(final JdbcTemplate jdbcTemplate) { } @Override - public int saveAndGetId(final GameEntity game) { + public Optional saveAndGetId(final GameEntity game) { SqlParameterSource params = new BeanPropertySqlParameterSource(game); - return jdbcInsert.executeAndReturnKey(params).intValue(); + return Optional.of(jdbcInsert.executeAndReturnKey(params).intValue()); } } diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 2f1a95670..79fed62e7 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -37,7 +37,8 @@ public GamePlayResponseDto play(final GamePlayRequestDto gameRequest) { game.play(); final GameEntity gameEntity = racingGameMapper.toGameEntity(gameRequest.getCount()); - final int gameId = gameDao.saveAndGetId(gameEntity); + final int gameId = gameDao.saveAndGetId(gameEntity) + .orElseThrow(() -> new IllegalArgumentException("게임 저장에 실패하였습니다.")); final List carEntities = racingGameMapper.toCarEntities(game, gameId); carDao.saveAll(carEntities); diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 77cc15752..805f283cf 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -35,7 +35,7 @@ void setUp() { @Test void saveAll_메서드는_입력받은_플레이어를_전부_저장한다() { // given - final int gameId = gameDao.saveAndGetId(new GameEntity(3)); + final Integer gameId = gameDao.saveAndGetId(new GameEntity(3)).get(); final List players = List.of(new CarEntity("car1", 1, true, gameId)); // when diff --git a/src/test/java/racingcar/dao/GameJdbcDaoTest.java b/src/test/java/racingcar/dao/GameJdbcDaoTest.java index e5565f31e..9a62d84e7 100644 --- a/src/test/java/racingcar/dao/GameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/GameJdbcDaoTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; @@ -34,9 +35,9 @@ void setUp() { final GameEntity game = new GameEntity(5); // when - final int id = gameDao.saveAndGetId(game); + final Optional gameId = gameDao.saveAndGetId(game); // then - assertThat(id).isPositive(); + assertThat(gameId.isPresent()).isTrue(); } } diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 9bfd2c414..3a0fe33b7 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -2,8 +2,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; import java.util.List; +import java.util.Optional; import org.assertj.core.util.Lists; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; @@ -35,6 +38,7 @@ class RacingGameServiceTest { carDao ); final GamePlayRequestDto gameRequest = new GamePlayRequestDto(List.of("브리", "비버", "허브"), 1); + given(gameDao.saveAndGetId(any())).willReturn(Optional.of(1)); // when final GamePlayResponseDto gameResponse = racingGameService.play(gameRequest); From b022338410cbb3aa6bade4f90a89cef82003b245 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 01:14:32 +0900 Subject: [PATCH 17/36] =?UTF-8?q?feat:=20CarDao=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/CarDao.java | 2 + src/main/java/racingcar/dao/CarJdbcDao.java | 16 ++++++++ .../java/racingcar/dao/ConsoleCarDao.java | 5 +++ src/main/java/racingcar/entity/CarEntity.java | 13 ++++--- .../java/racingcar/dao/CarJdbcDaoTest.java | 37 ++++++++++++++++--- 5 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java index c9a13ae6e..8b1924574 100644 --- a/src/main/java/racingcar/dao/CarDao.java +++ b/src/main/java/racingcar/dao/CarDao.java @@ -5,4 +5,6 @@ public interface CarDao { void saveAll(final List players); + + List findAll(); } diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index f757b2890..69526a943 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -2,6 +2,7 @@ import java.util.List; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Component; @@ -9,9 +10,18 @@ @Component public class CarJdbcDao implements CarDao { + private final JdbcTemplate jdbcTemplate; private final SimpleJdbcInsert simpleJdbcInsert; + private final RowMapper rowMapper = (resultSet, rowNum) -> new CarEntity( + resultSet.getInt("id"), + resultSet.getString("name"), + resultSet.getInt("position"), + resultSet.getBoolean("winner"), + resultSet.getInt("game_id") + ); public CarJdbcDao(final JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate) .withTableName("Car") .usingGeneratedKeyColumns("id"); @@ -23,4 +33,10 @@ public void saveAll(final List cars) { .toArray(BeanPropertySqlParameterSource[]::new); simpleJdbcInsert.executeBatch(parameterSources); } + + @Override + public List findAll() { + final String sql = "SELECT * FROM CAR"; + return jdbcTemplate.query(sql, rowMapper); + } } diff --git a/src/main/java/racingcar/dao/ConsoleCarDao.java b/src/main/java/racingcar/dao/ConsoleCarDao.java index 8f2054140..e7b3eb671 100644 --- a/src/main/java/racingcar/dao/ConsoleCarDao.java +++ b/src/main/java/racingcar/dao/ConsoleCarDao.java @@ -7,4 +7,9 @@ public class ConsoleCarDao implements CarDao { @Override public void saveAll(final List players) { } + + @Override + public List findAll() { + return List.of(); + } } diff --git a/src/main/java/racingcar/entity/CarEntity.java b/src/main/java/racingcar/entity/CarEntity.java index c2ab2c1a0..ee5630b62 100644 --- a/src/main/java/racingcar/entity/CarEntity.java +++ b/src/main/java/racingcar/entity/CarEntity.java @@ -3,11 +3,14 @@ import racingcar.domain.Car; public class CarEntity { - private final Integer id; - private final String name; - private final int position; - private final boolean winner; - private final Integer gameId; + private Integer id; + private String name; + private int position; + private boolean winner; + private Integer gameId; + + public CarEntity() { + } public CarEntity(final String name, final int position, final boolean winner, final Integer gameId) { this(null, name, position, winner, gameId); diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 805f283cf..434595c62 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -1,6 +1,7 @@ package racingcar.dao; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -33,17 +34,41 @@ void setUp() { } @Test - void saveAll_메서드는_입력받은_플레이어를_전부_저장한다() { + void saveAll_메서드는_입력받은_차를_전부_저장한다() { // given final Integer gameId = gameDao.saveAndGetId(new GameEntity(3)).get(); - final List players = List.of(new CarEntity("car1", 1, true, gameId)); + final List cars = List.of(new CarEntity("car1", 1, true, gameId)); // when - carDao.saveAll(players); + carDao.saveAll(cars); // then - final String sql = "select count(*) from car"; - final int count = jdbcTemplate.queryForObject(sql, Integer.class); - assertThat(count).isEqualTo(1); + final List result = carDao.findAll(); + assertThat(result).hasSize(1); + } + + @Test + void findAll_메서드는_모든_차를_반환한다() { + // given + final Integer gameId = gameDao.saveAndGetId(new GameEntity(3)).get(); + final List cars = List.of( + new CarEntity("car1", 1, false, gameId), + new CarEntity("car2", 3, true, gameId) + ); + carDao.saveAll(cars); + + // when + final List result = carDao.findAll(); + + // then + final CarEntity carEntity = result.get(0); + assertAll( + () -> assertThat(result).hasSize(2), + () -> assertThat(carEntity.getId()).isPositive(), + () -> assertThat(carEntity.getName()).isEqualTo("car1"), + () -> assertThat(carEntity.getPosition()).isEqualTo(1), + () -> assertThat(carEntity.isWinner()).isEqualTo(false), + () -> assertThat(carEntity.getGameId()).isEqualTo(gameId) + ); } } From 13b849a726d2808d959b6b4c88f332a7298b6165 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 01:48:34 +0900 Subject: [PATCH 18/36] =?UTF-8?q?feat:=20Car=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=9B=EC=95=84=20GamePlayResponseDto=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=EB=A1=9C=20=EB=A7=A4=ED=95=91=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../racingcar/service/RacingGameMapper.java | 24 ++++++++++++ .../service/RacingGameMapperTest.java | 37 ++++++++++++++++--- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/main/java/racingcar/service/RacingGameMapper.java b/src/main/java/racingcar/service/RacingGameMapper.java index 13c2f9c05..a4747514c 100644 --- a/src/main/java/racingcar/service/RacingGameMapper.java +++ b/src/main/java/racingcar/service/RacingGameMapper.java @@ -4,9 +4,13 @@ import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.springframework.stereotype.Component; import racingcar.domain.RacingGame; +import racingcar.dto.CarDto; +import racingcar.dto.GamePlayResponseDto; import racingcar.entity.CarEntity; import racingcar.entity.GameEntity; @@ -23,4 +27,24 @@ public List toCarEntities(final RacingGame racingGame, final int game .map(car -> CarEntity.of(car, winners.contains(car.getName()), gameId)) .collect(toList()); } + + public List toGamePlayResponseDtos(final List cars) { + final Map> gameIdByCars = cars.stream() + .collect(Collectors.groupingBy(CarEntity::getGameId)); + + return gameIdByCars.values().stream() + .map(this::mapToGamePlayResponseDto) + .collect(toList()); + } + + private GamePlayResponseDto mapToGamePlayResponseDto(final List carEntities) { + final List winners = carEntities.stream() + .filter(CarEntity::isWinner) + .map(CarEntity::getName) + .collect(toList()); + final List carDtos = carEntities.stream() + .map(car -> new CarDto(car.getName(), car.getPosition())) + .collect(toList()); + return new GamePlayResponseDto(winners, carDtos); + } } diff --git a/src/test/java/racingcar/service/RacingGameMapperTest.java b/src/test/java/racingcar/service/RacingGameMapperTest.java index a0fdbe3a3..ced1b38f3 100644 --- a/src/test/java/racingcar/service/RacingGameMapperTest.java +++ b/src/test/java/racingcar/service/RacingGameMapperTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import racingcar.domain.RacingGame; +import racingcar.dto.GamePlayResponseDto; import racingcar.entity.CarEntity; import racingcar.entity.GameEntity; import racingcar.utils.TestNumberGenerator; @@ -26,10 +27,10 @@ public class RacingGameMapperTest { final int trial = 5; // when - final GameEntity gameEntity = racingGameMapper.toGameEntity(trial); + final GameEntity result = racingGameMapper.toGameEntity(trial); // then - assertThat(gameEntity.getTrial()).isEqualTo(5); + assertThat(result.getTrial()).isEqualTo(5); } @Test @@ -41,13 +42,37 @@ public class RacingGameMapperTest { final int gameId = 1; // when - final List carEntities = racingGameMapper.toCarEntities(racingGame, gameId); + final List result = racingGameMapper.toCarEntities(racingGame, gameId); // then assertAll( - () -> assertThat(carEntities).hasSize(2), - () -> assertThat(carEntities.get(0).getName()).isEqualTo("비버"), - () -> assertThat(carEntities.get(0).getPosition()).isEqualTo(1) + () -> assertThat(result).hasSize(2), + () -> assertThat(result.get(0).getName()).isEqualTo("비버"), + () -> assertThat(result.get(0).getPosition()).isEqualTo(1) + ); + } + + @Test + void toGamePlayResponseDtos_메서드는_차_엔티티들을_받아_GamePlayResponseDto_리스트를_반환한다() { + // given + final List carEntities = List.of( + new CarEntity("car1", 4, false, 1), + new CarEntity("car2", 5, true, 1), + new CarEntity("car1", 5, true, 2), + new CarEntity("car2", 5, true, 2), + new CarEntity("car3", 2, false, 2) + ); + + // when + final List result = racingGameMapper.toGamePlayResponseDtos(carEntities); + + // then + assertAll( + () -> assertThat(result).hasSize(2), + () -> assertThat(result.get(0).getRacingCars()).hasSize(2), + () -> assertThat(result.get(0).getWinners()).hasSize(1), + () -> assertThat(result.get(1).getRacingCars()).hasSize(3), + () -> assertThat(result.get(1).getWinners()).hasSize(2) ); } } From 29f81894b520c3ba003ff508a603fa23e98a445f Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 01:56:52 +0900 Subject: [PATCH 19/36] =?UTF-8?q?feat:=20RacingGameService=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../racingcar/service/RacingGameService.java | 6 ++ .../service/RacingGameServiceTest.java | 55 +++++++++++++++---- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 79fed62e7..f686d26de 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -45,4 +45,10 @@ public GamePlayResponseDto play(final GamePlayRequestDto gameRequest) { return GamePlayResponseDto.of(game.findWinners(), game.getCars()); } + + @Transactional + public List findAll() { + final List result = carDao.findAll(); + return racingGameMapper.toGamePlayResponseDtos(result); + } } diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 3a0fe33b7..1748f5a23 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Optional; import org.assertj.core.util.Lists; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; @@ -18,35 +19,69 @@ import racingcar.domain.NumberGenerator; import racingcar.dto.GamePlayRequestDto; import racingcar.dto.GamePlayResponseDto; +import racingcar.entity.CarEntity; import racingcar.utils.TestNumberGenerator; @DisplayName("RacingGameService 클래스") @DisplayNameGeneration(ReplaceUnderscores.class) class RacingGameServiceTest { - @Test - void play_메서드는_자동차_경주를_진행한다() { - // given - final GameDao gameDao = Mockito.mock(GameDao.class); - final CarDao carDao = Mockito.mock(CarDao.class); + private GameDao gameDao; + private CarDao carDao; + private RacingGameService racingGameService; + + @BeforeEach + void setUp() { + gameDao = Mockito.mock(GameDao.class); + carDao = Mockito.mock(CarDao.class); final NumberGenerator numberGenerator = new TestNumberGenerator(Lists.newArrayList(4, 3, 3)); final RacingGameMapper racingGameMapper = new RacingGameMapper(); - final RacingGameService racingGameService = new RacingGameService( + racingGameService = new RacingGameService( numberGenerator, racingGameMapper, gameDao, carDao ); - final GamePlayRequestDto gameRequest = new GamePlayRequestDto(List.of("브리", "비버", "허브"), 1); + } + + @Test + void play_메서드는_자동차_경주를_진행한다() { + // given + final GamePlayRequestDto request = new GamePlayRequestDto(List.of("브리", "비버", "허브"), 1); given(gameDao.saveAndGetId(any())).willReturn(Optional.of(1)); // when - final GamePlayResponseDto gameResponse = racingGameService.play(gameRequest); + final GamePlayResponseDto result = racingGameService.play(request); + + // then + assertAll( + () -> assertThat(result.getWinners()).containsExactly("브리"), + () -> assertThat(result.getRacingCars()).hasSize(3) + ); + } + + @Test + void findAll_메서드는_게임_결과를_반환한다() { + // given + List carEntities = List.of( + new CarEntity("car1", 5, true, 1), + new CarEntity("car2", 5, true, 1), + new CarEntity("car1", 5, true, 2), + new CarEntity("car2", 5, true, 2), + new CarEntity("car3", 2, false, 2) + ); + given(carDao.findAll()).willReturn(carEntities); + + // when + final List result = racingGameService.findAll(); // then assertAll( - () -> assertThat(gameResponse.getWinners()).containsExactly("브리"), - () -> assertThat(gameResponse.getRacingCars()).hasSize(3) + () -> assertThat(result).hasSize(2), + () -> assertThat(result.get(0).getRacingCars()).hasSize(2), + () -> assertThat(result.get(0).getWinners()).hasSize(2), + () -> assertThat(result.get(1).getRacingCars()).hasSize(3), + () -> assertThat(result.get(1).getWinners()).hasSize(2) ); } } From f2f7d91603a1b693f55119ecb346faabbc3d3f89 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 01:57:16 +0900 Subject: [PATCH 20/36] =?UTF-8?q?feat:=20RacingGameController=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/WebRacingGameController.java | 7 ++ src/main/resources/static/list.html | 98 +++++++++---------- .../WebRacingGameControllerTest.java | 32 ++++++ 3 files changed, 88 insertions(+), 49 deletions(-) diff --git a/src/main/java/racingcar/controller/WebRacingGameController.java b/src/main/java/racingcar/controller/WebRacingGameController.java index b3027f86f..e34230515 100644 --- a/src/main/java/racingcar/controller/WebRacingGameController.java +++ b/src/main/java/racingcar/controller/WebRacingGameController.java @@ -1,5 +1,7 @@ package racingcar.controller; +import java.util.List; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -19,4 +21,9 @@ public WebRacingGameController(final RacingGameService racingGameService) { public GamePlayResponseDto plays(@RequestBody final GamePlayRequestDto request) { return racingGameService.play(request); } + + @GetMapping("/plays") + public List findAll() { + return racingGameService.findAll(); + } } diff --git a/src/main/resources/static/list.html b/src/main/resources/static/list.html index 9912dd20f..61f21cc66 100644 --- a/src/main/resources/static/list.html +++ b/src/main/resources/static/list.html @@ -1,72 +1,72 @@ - - 자동차 경주 게임 이력 조회 - + + 자동차 경주 게임 이력 조회 +
-

Racing Car

-
- - -
+

Racing Car

+
+ + +

자동차 경주 게임 이력 조회

- - - - - - - + + + + + + +
이동 횟수우승자
이동 횟수우승자
diff --git a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java index 3bb94c179..572533b97 100644 --- a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java @@ -5,6 +5,7 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.any; import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -63,4 +64,35 @@ class WebRacingGameControllerTest { .andExpect(jsonPath("$.racingCars[0].position").value(1)) .andDo(print()); } + + @Test + void findAll_메서드는_게임_결과를_반환한다() throws Exception { + // given + final List response = List.of( + new GamePlayResponseDto( + List.of("비버"), + List.of( + new CarDto("비버", 5), + new CarDto("허브", 0), + new CarDto("허브분신", 0) + ) + ), + new GamePlayResponseDto( + List.of("허브", "다즐"), + List.of(new CarDto("허브", 2), new CarDto("다즐", 2)) + ) + ); + given(racingGameService.findAll()).willReturn(response); + + // expect + mockMvc.perform(get("/plays") + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].winners[0]").value("비버")) + .andExpect(jsonPath("$[0].racingCars", hasSize(3))) + .andExpect(jsonPath("$[0].racingCars[0].name").value("비버")) + .andExpect(jsonPath("$[0].racingCars[0].position").value(5)) + .andDo(print()); + } } From c8b5b21a31d8098e59b3684811ef289255b840a2 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 01:57:16 +0900 Subject: [PATCH 21/36] =?UTF-8?q?feat:=20RacingGameController=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- .../controller/WebRacingGameController.java | 7 ++ src/main/resources/static/list.html | 98 +++++++++---------- .../WebRacingGameControllerTest.java | 32 ++++++ 4 files changed, 90 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 40f80a11a..4a8f86651 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ - [x] 게임 실행 요청 구현 - [x] 게임 저장 기능 구현 - [x] 자동차 저장 기능 구현 -- [ ] 게임 조회 기능 - - [ ] 게임 조회 요청 구현 +- [x] 게임 조회 기능 + - [x] 게임 조회 요청 구현 - [x] 콘솔 애플리케이션 출력 변경 - [x] 중간 과정을 출력하는 로직 제거 - [x] 우승자와 플레이어별 최종 이동거리를 출력하도록 수정 diff --git a/src/main/java/racingcar/controller/WebRacingGameController.java b/src/main/java/racingcar/controller/WebRacingGameController.java index b3027f86f..e34230515 100644 --- a/src/main/java/racingcar/controller/WebRacingGameController.java +++ b/src/main/java/racingcar/controller/WebRacingGameController.java @@ -1,5 +1,7 @@ package racingcar.controller; +import java.util.List; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -19,4 +21,9 @@ public WebRacingGameController(final RacingGameService racingGameService) { public GamePlayResponseDto plays(@RequestBody final GamePlayRequestDto request) { return racingGameService.play(request); } + + @GetMapping("/plays") + public List findAll() { + return racingGameService.findAll(); + } } diff --git a/src/main/resources/static/list.html b/src/main/resources/static/list.html index 9912dd20f..61f21cc66 100644 --- a/src/main/resources/static/list.html +++ b/src/main/resources/static/list.html @@ -1,72 +1,72 @@ - - 자동차 경주 게임 이력 조회 - + + 자동차 경주 게임 이력 조회 +
-

Racing Car

-
- - -
+

Racing Car

+
+ + +

자동차 경주 게임 이력 조회

- - - - - - - + + + + + + +
이동 횟수우승자
이동 횟수우승자
diff --git a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java index 3bb94c179..572533b97 100644 --- a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java @@ -5,6 +5,7 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.any; import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -63,4 +64,35 @@ class WebRacingGameControllerTest { .andExpect(jsonPath("$.racingCars[0].position").value(1)) .andDo(print()); } + + @Test + void findAll_메서드는_게임_결과를_반환한다() throws Exception { + // given + final List response = List.of( + new GamePlayResponseDto( + List.of("비버"), + List.of( + new CarDto("비버", 5), + new CarDto("허브", 0), + new CarDto("허브분신", 0) + ) + ), + new GamePlayResponseDto( + List.of("허브", "다즐"), + List.of(new CarDto("허브", 2), new CarDto("다즐", 2)) + ) + ); + given(racingGameService.findAll()).willReturn(response); + + // expect + mockMvc.perform(get("/plays") + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].winners[0]").value("비버")) + .andExpect(jsonPath("$[0].racingCars", hasSize(3))) + .andExpect(jsonPath("$[0].racingCars[0].name").value("비버")) + .andExpect(jsonPath("$[0].racingCars[0].position").value(5)) + .andDo(print()); + } } From 5bde85a88eba592c19e52990729755bb8318bf94 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 08:52:59 +0900 Subject: [PATCH 22/36] =?UTF-8?q?feat:=20request=EC=97=90=20validation=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 19 +++++----- .../controller/WebRacingGameController.java | 3 +- .../racingcar/dto/GamePlayRequestDto.java | 5 +++ .../exception/ExceptionResponse.java | 13 +++++++ .../exception/RacingGameExceptionHandler.java | 35 +++++++++++++++++++ 5 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 src/main/java/racingcar/exception/ExceptionResponse.java create mode 100644 src/main/java/racingcar/exception/RacingGameExceptionHandler.java diff --git a/build.gradle b/build.gradle index 721b6af13..ddcffb8b5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,23 +1,24 @@ plugins { - id 'java' - id 'org.springframework.boot' version '2.7.9' - id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'java' + id 'org.springframework.boot' version '2.7.9' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' } sourceCompatibility = '11' repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + testImplementation 'org.springframework.boot:spring-boot-starter-test' - implementation 'org.springframework.boot:spring-boot-starter-jdbc' - runtimeOnly 'com.h2database:h2' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + runtimeOnly 'com.h2database:h2' } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } diff --git a/src/main/java/racingcar/controller/WebRacingGameController.java b/src/main/java/racingcar/controller/WebRacingGameController.java index e34230515..184759cfc 100644 --- a/src/main/java/racingcar/controller/WebRacingGameController.java +++ b/src/main/java/racingcar/controller/WebRacingGameController.java @@ -1,6 +1,7 @@ package racingcar.controller; import java.util.List; +import javax.validation.Valid; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -18,7 +19,7 @@ public WebRacingGameController(final RacingGameService racingGameService) { } @PostMapping("/plays") - public GamePlayResponseDto plays(@RequestBody final GamePlayRequestDto request) { + public GamePlayResponseDto play(@RequestBody @Valid final GamePlayRequestDto request) { return racingGameService.play(request); } diff --git a/src/main/java/racingcar/dto/GamePlayRequestDto.java b/src/main/java/racingcar/dto/GamePlayRequestDto.java index 8a89aa232..527d39fca 100644 --- a/src/main/java/racingcar/dto/GamePlayRequestDto.java +++ b/src/main/java/racingcar/dto/GamePlayRequestDto.java @@ -1,9 +1,14 @@ package racingcar.dto; import java.util.List; +import javax.validation.constraints.NotEmpty; +import org.hibernate.validator.constraints.Range; public class GamePlayRequestDto { + @NotEmpty(message = "참가자를 입력해주세요.") private final List names; + + @Range(min = 1, max = 10000, message = "{min}이상, {max}이하의 시도 횟수를 입력해주세요.") private final int count; public GamePlayRequestDto(final List names, final int count) { diff --git a/src/main/java/racingcar/exception/ExceptionResponse.java b/src/main/java/racingcar/exception/ExceptionResponse.java new file mode 100644 index 000000000..1048405af --- /dev/null +++ b/src/main/java/racingcar/exception/ExceptionResponse.java @@ -0,0 +1,13 @@ +package racingcar.exception; + +public class ExceptionResponse { + private final String message; + + public ExceptionResponse(final String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/racingcar/exception/RacingGameExceptionHandler.java b/src/main/java/racingcar/exception/RacingGameExceptionHandler.java new file mode 100644 index 000000000..4cf5d05c2 --- /dev/null +++ b/src/main/java/racingcar/exception/RacingGameExceptionHandler.java @@ -0,0 +1,35 @@ +package racingcar.exception; + +import java.util.stream.Collectors; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class RacingGameExceptionHandler { + private static final String DELIMITER = ", "; + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ExceptionResponse(e.getMessage())); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidException(final MethodArgumentNotValidException e) { + final String errorMessage = e.getFieldErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.joining(DELIMITER)); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ExceptionResponse(errorMessage)); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ExceptionResponse("서버가 응답할 수 없습니다.")); + } +} From ff564cfe76e61f4d6bede67c0472a0d1d99ce24a Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 09:04:38 +0900 Subject: [PATCH 23/36] =?UTF-8?q?remote:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/WebRacingGameApplication.java | 7 +++---- src/main/java/racingcar/entity/CarEntity.java | 3 --- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/racingcar/WebRacingGameApplication.java b/src/main/java/racingcar/WebRacingGameApplication.java index 6b61416a8..370d5d06e 100644 --- a/src/main/java/racingcar/WebRacingGameApplication.java +++ b/src/main/java/racingcar/WebRacingGameApplication.java @@ -6,8 +6,7 @@ @SpringBootApplication public class WebRacingGameApplication { - public static void main(String[] args) { - SpringApplication.run(WebRacingGameApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(WebRacingGameApplication.class, args); + } } diff --git a/src/main/java/racingcar/entity/CarEntity.java b/src/main/java/racingcar/entity/CarEntity.java index ee5630b62..ab4544a38 100644 --- a/src/main/java/racingcar/entity/CarEntity.java +++ b/src/main/java/racingcar/entity/CarEntity.java @@ -9,9 +9,6 @@ public class CarEntity { private boolean winner; private Integer gameId; - public CarEntity() { - } - public CarEntity(final String name, final int position, final boolean winner, final Integer gameId) { this(null, name, position, winner, gameId); } From 55cdf91cc4a5be6abd54c002a7d4f49f0481acef Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 09:05:09 +0900 Subject: [PATCH 24/36] =?UTF-8?q?refactor:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dto/GamePlayRequestDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/racingcar/dto/GamePlayRequestDto.java b/src/main/java/racingcar/dto/GamePlayRequestDto.java index 527d39fca..6d857e166 100644 --- a/src/main/java/racingcar/dto/GamePlayRequestDto.java +++ b/src/main/java/racingcar/dto/GamePlayRequestDto.java @@ -5,7 +5,7 @@ import org.hibernate.validator.constraints.Range; public class GamePlayRequestDto { - @NotEmpty(message = "참가자를 입력해주세요.") + @NotEmpty(message = "차 이름을 입력해주세요.") private final List names; @Range(min = 1, max = 10000, message = "{min}이상, {max}이하의 시도 횟수를 입력해주세요.") From ee97a59e4d803e3386bb49ce0648db7163b52136 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 09:05:25 +0900 Subject: [PATCH 25/36] =?UTF-8?q?refactor:=20simpleJdbcInsert=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/CarJdbcDao.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index 69526a943..9a262095c 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -11,7 +11,7 @@ @Component public class CarJdbcDao implements CarDao { private final JdbcTemplate jdbcTemplate; - private final SimpleJdbcInsert simpleJdbcInsert; + private final SimpleJdbcInsert jdbcInsert; private final RowMapper rowMapper = (resultSet, rowNum) -> new CarEntity( resultSet.getInt("id"), resultSet.getString("name"), @@ -22,7 +22,7 @@ public class CarJdbcDao implements CarDao { public CarJdbcDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; - this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate) + this.jdbcInsert = new SimpleJdbcInsert(jdbcTemplate) .withTableName("Car") .usingGeneratedKeyColumns("id"); } @@ -31,7 +31,7 @@ public void saveAll(final List cars) { final BeanPropertySqlParameterSource[] parameterSources = cars.stream() .map(BeanPropertySqlParameterSource::new) .toArray(BeanPropertySqlParameterSource[]::new); - simpleJdbcInsert.executeBatch(parameterSources); + jdbcInsert.executeBatch(parameterSources); } @Override From a957c5c43aa59453f098606612bca33053cb001f Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 09:19:34 +0900 Subject: [PATCH 26/36] =?UTF-8?q?refactor:=20Controller=EC=97=90=EC=84=9C?= =?UTF-8?q?=20ResponseEntity=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/WebRacingGameController.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/racingcar/controller/WebRacingGameController.java b/src/main/java/racingcar/controller/WebRacingGameController.java index 184759cfc..2759e0905 100644 --- a/src/main/java/racingcar/controller/WebRacingGameController.java +++ b/src/main/java/racingcar/controller/WebRacingGameController.java @@ -2,6 +2,8 @@ import java.util.List; import javax.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -19,12 +21,15 @@ public WebRacingGameController(final RacingGameService racingGameService) { } @PostMapping("/plays") - public GamePlayResponseDto play(@RequestBody @Valid final GamePlayRequestDto request) { - return racingGameService.play(request); + public ResponseEntity play(@RequestBody @Valid final GamePlayRequestDto request) { + final GamePlayResponseDto result = racingGameService.play(request); + return ResponseEntity.status(HttpStatus.CREATED) + .body(result); } @GetMapping("/plays") - public List findAll() { - return racingGameService.findAll(); + public ResponseEntity> findAll() { + final List result = racingGameService.findAll(); + return ResponseEntity.ok(result); } } From fbd5d7f03ca3396f6880d868765f50b213c3a11b Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 11:14:44 +0900 Subject: [PATCH 27/36] =?UTF-8?q?feat:=20Override=20=EB=B9=A0=EC=A7=84=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/CarJdbcDao.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index 9a262095c..d7867e38f 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -27,6 +27,7 @@ public CarJdbcDao(final JdbcTemplate jdbcTemplate) { .usingGeneratedKeyColumns("id"); } + @Override public void saveAll(final List cars) { final BeanPropertySqlParameterSource[] parameterSources = cars.stream() .map(BeanPropertySqlParameterSource::new) From 9934292c6f1e5b821502b75c5f8e84fa94a12900 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 18 Apr 2023 11:16:10 +0900 Subject: [PATCH 28/36] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=98=EB=AA=BB=20=EC=9E=91=EC=84=B1=EB=90=9C=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/controller/WebRacingGameControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java index 572533b97..fef4c79db 100644 --- a/src/test/java/racingcar/controller/WebRacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/WebRacingGameControllerTest.java @@ -57,7 +57,7 @@ class WebRacingGameControllerTest { mockMvc.perform(post("/plays") .content(request) .contentType(APPLICATION_JSON)) - .andExpect(status().isOk()) + .andExpect(status().isCreated()) .andExpect(jsonPath("$.winners[0]").value("비버")) .andExpect(jsonPath("racingCars", hasSize(2))) .andExpect(jsonPath("$.racingCars[0].name").value("비버")) From 3781e599e18eedaa4ed836a6b5ae0125ca55d245 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Wed, 19 Apr 2023 09:06:27 +0900 Subject: [PATCH 29/36] =?UTF-8?q?feat:=20CarEntity=EC=9D=98=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20final=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/entity/CarEntity.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/racingcar/entity/CarEntity.java b/src/main/java/racingcar/entity/CarEntity.java index ab4544a38..c2ab2c1a0 100644 --- a/src/main/java/racingcar/entity/CarEntity.java +++ b/src/main/java/racingcar/entity/CarEntity.java @@ -3,11 +3,11 @@ import racingcar.domain.Car; public class CarEntity { - private Integer id; - private String name; - private int position; - private boolean winner; - private Integer gameId; + private final Integer id; + private final String name; + private final int position; + private final boolean winner; + private final Integer gameId; public CarEntity(final String name, final int position, final boolean winner, final Integer gameId) { this(null, name, position, winner, gameId); From 446e7e441f4f76c378b0dfdefa05b27d026d6ce0 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Wed, 19 Apr 2023 09:06:51 +0900 Subject: [PATCH 30/36] =?UTF-8?q?test:=20=EA=B2=80=EC=A6=9D=EB=B6=80=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/racingcar/dao/CarJdbcDaoTest.java | 4 ++-- src/test/java/racingcar/dao/GameJdbcDaoTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 434595c62..44a67a2a8 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -59,7 +59,7 @@ void setUp() { // when final List result = carDao.findAll(); - + // then final CarEntity carEntity = result.get(0); assertAll( @@ -67,7 +67,7 @@ void setUp() { () -> assertThat(carEntity.getId()).isPositive(), () -> assertThat(carEntity.getName()).isEqualTo("car1"), () -> assertThat(carEntity.getPosition()).isEqualTo(1), - () -> assertThat(carEntity.isWinner()).isEqualTo(false), + () -> assertThat(carEntity.isWinner()).isFalse(), () -> assertThat(carEntity.getGameId()).isEqualTo(gameId) ); } diff --git a/src/test/java/racingcar/dao/GameJdbcDaoTest.java b/src/test/java/racingcar/dao/GameJdbcDaoTest.java index 9a62d84e7..6d207158c 100644 --- a/src/test/java/racingcar/dao/GameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/GameJdbcDaoTest.java @@ -38,6 +38,6 @@ void setUp() { final Optional gameId = gameDao.saveAndGetId(game); // then - assertThat(gameId.isPresent()).isTrue(); + assertThat(gameId).isPresent(); } } From 9e8e75504cb50bb895788b8c05c78a237ae80c3d Mon Sep 17 00:00:00 2001 From: greeng00se Date: Wed, 19 Apr 2023 09:13:44 +0900 Subject: [PATCH 31/36] =?UTF-8?q?refactor:=20DAO=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B9=88=20=EC=95=84=EC=9D=B4=EB=94=94=EB=A5=BC=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=EB=B0=9B=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20IllegalState?= =?UTF-8?q?Exception=EC=9D=84=20=EB=8D=98=EC=A7=80=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/service/RacingGameService.java | 2 +- .../racingcar/service/RacingGameServiceTest.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index f686d26de..6d6a18b35 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -38,7 +38,7 @@ public GamePlayResponseDto play(final GamePlayRequestDto gameRequest) { final GameEntity gameEntity = racingGameMapper.toGameEntity(gameRequest.getCount()); final int gameId = gameDao.saveAndGetId(gameEntity) - .orElseThrow(() -> new IllegalArgumentException("게임 저장에 실패하였습니다.")); + .orElseThrow(() -> new IllegalStateException("게임 저장에 실패하였습니다.")); final List carEntities = racingGameMapper.toCarEntities(game, gameId); carDao.saveAll(carEntities); diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 1748f5a23..05784bfb3 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -1,6 +1,7 @@ package racingcar.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -60,6 +61,18 @@ void setUp() { ); } + @Test + void play_메서드_저장_후_빈_아이디를_반환받는_경우_IllegalStateException을_던진다() { + // given + final GamePlayRequestDto request = new GamePlayRequestDto(List.of("브리", "비버", "허브"), 1); + given(gameDao.saveAndGetId(any())).willReturn(Optional.empty()); + + // expect + assertThatThrownBy(() -> racingGameService.play(request)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("게임 저장에 실패하였습니다."); + } + @Test void findAll_메서드는_게임_결과를_반환한다() { // given From 8adb76a033725f8696aade4a08d0c3661dfbe18d Mon Sep 17 00:00:00 2001 From: greeng00se Date: Wed, 19 Apr 2023 09:17:12 +0900 Subject: [PATCH 32/36] =?UTF-8?q?feat:=20IllegalStateException=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=ED=95=B8=EB=93=A4=EB=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../racingcar/exception/RacingGameExceptionHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/racingcar/exception/RacingGameExceptionHandler.java b/src/main/java/racingcar/exception/RacingGameExceptionHandler.java index 4cf5d05c2..c6ddf40be 100644 --- a/src/main/java/racingcar/exception/RacingGameExceptionHandler.java +++ b/src/main/java/racingcar/exception/RacingGameExceptionHandler.java @@ -12,8 +12,8 @@ public class RacingGameExceptionHandler { private static final String DELIMITER = ", "; - @ExceptionHandler(IllegalArgumentException.class) - public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { + @ExceptionHandler({IllegalArgumentException.class, IllegalStateException.class}) + public ResponseEntity handleIllegalArgumentException(final RuntimeException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ExceptionResponse(e.getMessage())); } @@ -28,7 +28,7 @@ public ResponseEntity handleValidException(final MethodArgume } @ExceptionHandler(Exception.class) - public ResponseEntity handleException(Exception e) { + public ResponseEntity handleException(final Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ExceptionResponse("서버가 응답할 수 없습니다.")); } From f913f4ef940aeff5a003b7d36728decc4c1315f2 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Wed, 19 Apr 2023 15:44:00 +0900 Subject: [PATCH 33/36] =?UTF-8?q?feat:=20=ED=86=B5=ED=95=A9=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../racingcar/integration/RacingGameTest.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/test/java/racingcar/integration/RacingGameTest.java diff --git a/src/test/java/racingcar/integration/RacingGameTest.java b/src/test/java/racingcar/integration/RacingGameTest.java new file mode 100644 index 000000000..9e7b88a74 --- /dev/null +++ b/src/test/java/racingcar/integration/RacingGameTest.java @@ -0,0 +1,98 @@ +package racingcar.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.mockito.BDDMockito.given; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; +import racingcar.dao.CarDao; +import racingcar.dao.GameDao; +import racingcar.domain.NumberGenerator; +import racingcar.dto.GamePlayRequestDto; +import racingcar.entity.CarEntity; +import racingcar.entity.GameEntity; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +public class RacingGameTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private GameDao gameDao; + + @Autowired + private CarDao carDao; + + @MockBean + private NumberGenerator numberGenerator; + + @Test + void 게임을_진행한다() throws Exception { + // given + final GamePlayRequestDto gamePlayRequestDto = new GamePlayRequestDto(List.of("비버", "허브"), 3); + final String request = objectMapper.writeValueAsString(gamePlayRequestDto); + given(numberGenerator.generate()).willReturn(7, 3, 3, 4, 5, 2); + + // expect + mockMvc.perform(post("/plays") + .content(request) + .contentType(APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.winners", Matchers.contains("비버"))) + .andExpect(jsonPath("$.racingCars", hasSize(2))) + .andExpect(jsonPath("$.racingCars[0].name", is("비버"))) + .andExpect(jsonPath("$.racingCars[0].position").value(2)) + .andExpect(jsonPath("$.racingCars[1].name", is("허브"))) + .andExpect(jsonPath("$.racingCars[1].position").value(1)) + .andDo(print()); + assertThat(carDao.findAll()).hasSize(2); + } + + @Test + void 게임_결과를_반환한다() throws Exception { + // given + final Integer gameId = gameDao.saveAndGetId(new GameEntity(9999)).get(); + carDao.saveAll(List.of( + new CarEntity("비버", 5, true, gameId), + new CarEntity("허브", 4, false, gameId) + )); + + // expect + mockMvc.perform(get("/plays") + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].winners", Matchers.contains("비버"))) + .andExpect(jsonPath("$[0].racingCars", hasSize(2))) + .andExpect(jsonPath("$[0].racingCars[0].name", is("비버"))) + .andExpect(jsonPath("$[0].racingCars[0].position").value(5)) + .andExpect(jsonPath("$[0].racingCars[1].name", is("허브"))) + .andExpect(jsonPath("$[0].racingCars[1].position").value(4)) + .andDo(print()); + } +} From 57d98cd94c9d41141e2cb28f469d53cac78b9d25 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Wed, 19 Apr 2023 15:47:13 +0900 Subject: [PATCH 34/36] =?UTF-8?q?refactor:=20CarDao=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/CarDao.java | 2 +- src/main/java/racingcar/dao/ConsoleCarDao.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java index 8b1924574..eec333523 100644 --- a/src/main/java/racingcar/dao/CarDao.java +++ b/src/main/java/racingcar/dao/CarDao.java @@ -4,7 +4,7 @@ import racingcar.entity.CarEntity; public interface CarDao { - void saveAll(final List players); + void saveAll(final List cars); List findAll(); } diff --git a/src/main/java/racingcar/dao/ConsoleCarDao.java b/src/main/java/racingcar/dao/ConsoleCarDao.java index e7b3eb671..13aceca9b 100644 --- a/src/main/java/racingcar/dao/ConsoleCarDao.java +++ b/src/main/java/racingcar/dao/ConsoleCarDao.java @@ -5,7 +5,7 @@ public class ConsoleCarDao implements CarDao { @Override - public void saveAll(final List players) { + public void saveAll(final List cars) { } @Override From 4ac2929a14739e048e4ecec43beaca7a8cfb67d1 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Wed, 19 Apr 2023 15:48:58 +0900 Subject: [PATCH 35/36] =?UTF-8?q?feat:=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20tra?= =?UTF-8?q?nsactional=20readOnly=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/service/RacingGameService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 6d6a18b35..25c56f7a1 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -46,7 +46,7 @@ public GamePlayResponseDto play(final GamePlayRequestDto gameRequest) { return GamePlayResponseDto.of(game.findWinners(), game.getCars()); } - @Transactional + @Transactional(readOnly = true) public List findAll() { final List result = carDao.findAll(); return racingGameMapper.toGamePlayResponseDtos(result); From 04a45cd08a252208d66f6c3bfcce114ba85e4ce3 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Wed, 19 Apr 2023 16:11:08 +0900 Subject: [PATCH 36/36] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{RacingGameTest.java => RacingGameIntegrationTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/racingcar/integration/{RacingGameTest.java => RacingGameIntegrationTest.java} (98%) diff --git a/src/test/java/racingcar/integration/RacingGameTest.java b/src/test/java/racingcar/integration/RacingGameIntegrationTest.java similarity index 98% rename from src/test/java/racingcar/integration/RacingGameTest.java rename to src/test/java/racingcar/integration/RacingGameIntegrationTest.java index 9e7b88a74..d2b658cff 100644 --- a/src/test/java/racingcar/integration/RacingGameTest.java +++ b/src/test/java/racingcar/integration/RacingGameIntegrationTest.java @@ -35,7 +35,7 @@ @Transactional @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -public class RacingGameTest { +public class RacingGameIntegrationTest { @Autowired private MockMvc mockMvc;