Skip to content

Commit

Permalink
test : 사용자가 숨은그림찾기 게임 정보를 조회하는 로직 테스트코드 작성, Clock 객체 모킹하여 LocalDateTi…
Browse files Browse the repository at this point in the history
…me.now()에 주입 (CC-147) (#28)

* chore : LocalDateTime.now() 테스트코드 작성을 위한 TimeConfig 빈 등록 (CC-147)

* test :TimeConfig, Clock객체를 모킹한 테스트코드 작성 (CC-147)

* fix : 필요없는 출력문 제거 (CC-147)

* fix : GithubAction 컨테이너의 테스트 환경에서의 UTC 시차 테스트를 위한 코드 (CC-147)

* fix : 오타 수정 (CC-148)

* fix : 오타 수정 (CC-148)

* chore : 스프링부트 어플리케이션 Timezone KST로 설정 (CC-147)

* test : 단위테스트 Timezone KST로 설정 (CC-147)

* test : 단위테스트 Timezone KST로 설정한 후 LocalDateTime.now 호출로 변경 (CC-147)

* test : LocalDateTimeService ci 테스트용 코드 (CC-147)

* test : LocalDateTimeService ci 테스트용 코드 (CC-147)

* test : Clock.instant에 UTC+9의 시차가 정상적으로 적용되는지 확인하는 테스트코드 작성 (CC-147)
  • Loading branch information
j2noo authored and putdata committed Aug 14, 2024
1 parent c70cda2 commit 71f7a8f
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
package ai.softeer.caecae.findinggame.service;

import ai.softeer.caecae.findinggame.domain.dto.FindingGameDailyInfo;
import ai.softeer.caecae.findinggame.domain.dto.response.FindingGameInfoResponseDto;
import ai.softeer.caecae.findinggame.domain.entity.FindingGame;
import ai.softeer.caecae.findinggame.domain.enums.AnswerType;
import ai.softeer.caecae.findinggame.repository.FindGameDbRepository;
import ai.softeer.caecae.racinggame.domain.dto.request.RegisterFindingGamePeriodRequestDto;
import ai.softeer.caecae.findinggame.domain.dto.response.FindingGameInfoResponseDto;
import ai.softeer.caecae.racinggame.domain.dto.response.RegisterFindingGamePeriodResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -20,54 +17,8 @@
@RequiredArgsConstructor
public class FindingGameService {
private final FindGameDbRepository findGameDbRepository;

/**
* 어드민이 숨은캐스퍼찾기 게임 기간을 등록하는 로직
*
* @param req 게임 시작 날짜
* @return 게임 시작 날짜, 종료 날짜(+6일)
*/
@Transactional
public RegisterFindingGamePeriodResponseDto registerFindingGamePeriod(RegisterFindingGamePeriodRequestDto req) {
List<FindingGame> findingGames = findGameDbRepository.findAll();
// 등록된 게임 정보가 없으면 생성하기
if (findingGames.isEmpty()) {
findingGames = initFindingGames();
}

// 게임 정보 기간 업데이트
LocalDate date = req.startDate();
for (FindingGame findingGame : findingGames) {
findingGame.updateFindingGamePeriod(
date.atTime(15, 15),
date.plusDays(1).atTime(14, 15)
);
date = date.plusDays(1);
}


findGameDbRepository.saveAll(findingGames);

return RegisterFindingGamePeriodResponseDto.builder()
.startDate(req.startDate())
.endDate(req.startDate().plusDays(6))
.build();
}

// 7개의 숨은캐스퍼찾기 게임 정보 객체 초기화
private List<FindingGame> initFindingGames() {
List<FindingGame> findingGames = new ArrayList<>();
for (int day = 0; day < 7; day++) {
findingGames.add(
FindingGame.builder()
.questionImageUrl("no-image")
.numberOfWinners(315)
.answerType(AnswerType.UNSELECTED)
.build());
}
return findingGames;
}

private final Clock clock; // 테스트코드 의존성 주입을 위한 빈

/**
* 전체 게임 정보와, 최근/다음 게임의 인덱스를 반환하는 로직
*
Expand All @@ -76,12 +27,11 @@ private List<FindingGame> initFindingGames() {
@Transactional(readOnly = true)
public FindingGameInfoResponseDto getFindingGameInfo() {
List<FindingGame> findingGames = findGameDbRepository.findAllByOrderByStartTime();

int recentGameIndex = -1;
int nextGameIndex = -1;
for (int i = 0; i < findingGames.size(); i++) {
FindingGame findingGame = findingGames.get(i);
if (findingGame.getStartTime().isBefore(LocalDateTime.now())) {
if (findingGame.getStartTime().isBefore(LocalDateTime.now(clock))) {
recentGameIndex = i;
}
}
Expand All @@ -108,4 +58,12 @@ public FindingGameInfoResponseDto getFindingGameInfo() {
.nextGameIndex(nextGameIndex)
.build();
}

// 테스트용
public LocalDateTime testUtcClockInstant() {
findGameDbRepository.findAllByOrderByStartTime();
return LocalDateTime.now(clock);
}

}

22 changes: 22 additions & 0 deletions src/main/java/ai/softeer/caecae/global/config/TimeConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ai.softeer.caecae.global.config;

import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Clock;
import java.util.TimeZone;

@Configuration
public class TimeConfig {

@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}

@PostConstruct
void started() {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package ai.softeer.caecae.findinggame.service;

import ai.softeer.caecae.findinggame.domain.dto.response.FindingGameInfoResponseDto;
import ai.softeer.caecae.findinggame.domain.entity.FindingGame;
import ai.softeer.caecae.findinggame.domain.enums.AnswerType;
import ai.softeer.caecae.findinggame.repository.FindGameDbRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;

import static org.mockito.BDDMockito.given;

@ExtendWith(MockitoExtension.class)
class FindingGameServiceTest {

@InjectMocks
private FindingGameService findingGameService;

@Mock
private FindGameDbRepository findGameDbRepository;

@Mock
private Clock clock;

// 7일치의 숨은그림찾기 게임 정보
private final List<FindingGame> findingGames = new ArrayList<>();
private static LocalDateTime integrityDateTime;


@BeforeAll
static void timeSetUp() {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
integrityDateTime = LocalDateTime.of(2024, 5, 5, 10, 10);
}

@BeforeEach
void setUp() {
// 8/1 ~ 8/7 게임
LocalDateTime baseTime = LocalDateTime.of(2024, 8, 1, 15, 15);
for (int day = 0; day < 7; day++) {
findingGames.add(
FindingGame.builder()
.id(day + 1)
.questionImageUrl("url")
.startTime(baseTime.plusDays(day))
.endTime(baseTime.plusDays(day + 1).minusHours(1))
.numberOfWinners(315)
.answerType(AnswerType.PIXEL)
.build()
);
}

Mockito.when(findGameDbRepository.findAllByOrderByStartTime()).thenReturn(findingGames);

given(clock.getZone()).willReturn(ZoneId.systemDefault());
}

@Test
@DisplayName("LocalDateTime과 비교하여, Clock.instant의 시간에 TimeZone의 시차가 적용되는지 테스트 하기 - 같은 시간")
void testUtcClockInstant1() {
// Given
given(clock.instant()).willReturn(Instant.parse("2024-05-05T10:10:00Z"));

// When
// Clock을 사용하여 LocalDateTime 생성
LocalDateTime time = findingGameService.testUtcClockInstant();

// Then
Assertions.assertThat(time).isNotEqualTo(integrityDateTime);
}

@Test
@DisplayName("LocalDateTime과 Clock.instant의 시간에 UTC+9의 시차가 적용되는지 테스트 하기 - UTC+9")
void testUtcClockInstant2() {
// Given
given(clock.instant()).willReturn(Instant.parse("2024-05-05T10:10:00Z").minusSeconds(9 * 3600));

// When
// Clock을 사용하여 LocalDateTime 생성
LocalDateTime time = findingGameService.testUtcClockInstant();

// Then
Assertions.assertThat(time).isEqualTo(integrityDateTime);
}


@Test
@DisplayName("Given_일주일치 게임 정보 입력 When_게임 시작 전 Then_최근게임 인덱스가 -1")
void getFindingGameInfo_1() {
// Given
// LocalDateTime.now()에서 사용할 clock 객체 모킹
given(clock.instant()).willReturn(Instant.parse("2024-08-01T09:00:00Z").minusSeconds(9 * 3600));

// When
FindingGameInfoResponseDto findingGameInfo = findingGameService.getFindingGameInfo();

// Then
Assertions.assertThat(findingGameInfo).isNotNull();
Assertions.assertThat(findingGameInfo.recentGameIndex()).isEqualTo(-1);
Assertions.assertThat(findingGameInfo.nextGameIndex()).isEqualTo(0);
}

@Test
@DisplayName("Given_일주일치 게임 정보 입력 When_게임 진행 중(첫날) Then_최근게임 인덱스가 0")
void getFindingGameInfo_2() {
// Given
// LocalDateTime.now()에서 사용할 clock 객체 모킹
given(clock.instant()).willReturn(Instant.parse("2024-08-01T17:00:00Z").minusSeconds(9 * 3600));

// When
FindingGameInfoResponseDto findingGameInfo = findingGameService.getFindingGameInfo();

// Then
Assertions.assertThat(findingGameInfo).isNotNull();
Assertions.assertThat(findingGameInfo.recentGameIndex()).isEqualTo(0);
Assertions.assertThat(findingGameInfo.nextGameIndex()).isEqualTo(1);
}

@Test
@DisplayName("Given_일주일치 게임 정보 입력 When_게임 진행 중(마지막날) Then_최근게임 인덱스가 6")
void getFindingGameInfo_3() {
// Given
// LocalDateTime.now()에서 사용할 clock 객체 모킹
given(clock.instant()).willReturn(Instant.parse("2024-08-08T10:00:00Z").minusSeconds(9 * 3600));

// When
FindingGameInfoResponseDto findingGameInfo = findingGameService.getFindingGameInfo();

// Then
Assertions.assertThat(findingGameInfo).isNotNull();
Assertions.assertThat(findingGameInfo.recentGameIndex()).isEqualTo(6);
Assertions.assertThat(findingGameInfo.nextGameIndex()).isEqualTo(-1);
}

@Test
@DisplayName("Given_일주일치 게임 정보 입력 When_게임 진행 종료 Then_다음게임 인덱스가 -1")
void getFindingGameInfo_4() {
// Given
// LocalDateTime.now()에서 사용할 clock 객체 모킹
given(clock.instant()).willReturn(Instant.parse("2032-08-08T16:00:00Z").minusSeconds(9 * 3600));

// When
FindingGameInfoResponseDto findingGameInfo = findingGameService.getFindingGameInfo();

// Then
Assertions.assertThat(findingGameInfo).isNotNull();
Assertions.assertThat(findingGameInfo.recentGameIndex()).isEqualTo(6);
Assertions.assertThat(findingGameInfo.nextGameIndex()).isEqualTo(-1);
}
}

0 comments on commit 71f7a8f

Please sign in to comment.