개인 플젝 Unit Test 작성 중 LocalDate를 테스트에 적용해야 했다.
public double getPercentage(Long usersId) {
int findAll = todoRepository.findByDate(usersId, LocalDate.now());
int findAllDone = todoRepository.findByDateAndStatus(usersId, LocalDate.now());
...
}
Unit test 적용을 위해 LocalDate를 Mocking하려 했으나 mockito에서 when 메서드의 인자값으로 '호출이 되는 메소드'
를 넣어줘야 하는데 static메서드가 들어가서 인식이 되지 않았다.
doReturn(3).when(todoRepository)
.findByDate(anyLong(), LocalDate.now()); --> (x)
이를 해결할 수 있는 여러 방법이 있었으나 그 중 Clock를 이용하기로 하였다.
LocalDate.now()
자체를 Mocking 하는 방법도 있으나 이방법은 안티패턴이라고 한다
LocalDate.now()
는 기본 시간대의 시스템 시계에서 현재 날자를 가져온다고 한다.
이 방법은 clock이 하드코딩되어있어 테스트를 위한 대체clock
을 사용할 수가 없다고 한다...
@Configuration
public class TimeConfig {
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}
LocalDate.now() -> LocalDate.now(Clock)
@Transactional
@Service
@RequiredArgsConstructor
public class AttendService {
private final Clock clock;
public double getPercentage(Long usersId) {
LocalDate now = LocalDate.now(clock);
int findAll = todoRepository.findByDate(usersId, now);
int findAllDone = todoRepository.findByDateAndStatus(usersId, now);
...
}
}
@ExtendWith(MockitoExtension.class)
public class AttendServiceTest {
@Mock
private Clock clock;
private final static LocalDate LOCAL_DATE = LocalDate.of(2023, 2, 7);
...
@Test
void getPercentage() {
fixedClock = Clock.fixed(LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();
doReturn(3)
.when(todoRepository).findByDate(anyLong(), eq(LocalDate.now(fixedClock)));
doReturn(1)
.when(todoRepository).findByDateAndStatus(anyLong(), eq(LocalDate.now(fixedClock)));
}
}
...