안녕하세요 푸드 테크팀 백엔드 개발자 박형민입니다 :)
👏🏻 최근 프로젝트에서 특정일자와 오늘날짜의 차이를 구하는 로직을 구현해야했었는데, 이 날짜 로직에 LocalDate.now() 가 포함되어있어 오늘만 통과하는 테스트코드를 작성하게되어 곤란함이 있었습니다.
그래서 이번 포스팅에서는 제가 mocking 할 수 없는, LocalDateTime.now( ) 메소드를 어떻게 mocking 했는지에 대해 작성해보고자 합니다.
하지만 저희에게는 mocking 이라는 훌룡한 조롱수단이 있습니다.
LocalDate 객체를 mocking 하고 now() 메소드에 원하는 값을 반환시켜주면 항상 성공하는 테스트 케이스가 작성되겠죠
하지만 실패합니다.
그럼에도 불구하고 LocalDate 의 정적 메소드인 now()를 mocking 할 수 있는 몇가지 방법들이 존재합니다.
가장 쉬운 방법입니다.
수많은 블로그나, 레펀런스를 보면 보통 Mockito-inline 을 dependecy 에 추가해주어야한다고 이야기합니다.
//이런식으로 mocking 이 가능해집니다.
MockedStatic<LocalDate> localDateMockedStatic = Mockito.mockStatic(LocalDate.class);
localDateMockedStatic.when(LocalDate::now).thenReturn(date);
🔥 하지만, PowerMock을 지양해야하는 몆가지 이유가 존재합니다.
두번째 방법은 정적 메소드를 커스텀한 class 로 감싸는 방법입니다.
final 클래스라 mocking 이 불가능하고, 정적 메소드라 mocking 이 불가능하다면 새로운 객체에 해당 기능만을 담아 만들어 mocking 하는 방법도 있습니다.
👏🏻 간단하게 LocalDate.now() 를 감싼 객체를 만들었습니다.
public class DateUtil {
public LocalDate now() {
return LocalDate.now();
}
}
👍 Power Mock 을 사용하지도 않았고, 외부 라이브러리에 종속적이지도 않습니다 :)
🙅♂️ 하지만 이 방법은 저만을 위한 객체가 될 수 있기 때문에, 협업을 하는 상황에서 좋지 않다고 생각했습니다.
✔️ 마지막 방법이고 채택한 방법입니다.
진짜 오랫동안 LocalDate.now() 메소드를 째려보니(?) 1가지 묘한 점을 발견할 수 있었습니다.
static 메소드인 LocalDate.now() 메소드가 내부적으로 LocalDate.now(Clock clock) 메소드를 호출하면서 Clock 객체를 파라미터로 주입하고있었고
Clock 객체는 final class 도 아니면, now() 에서 호출하고잇는 instance() 메소드도 static 메소드가 아니였습니다.
👏🏻 즉! Clock 객체를 mocking 할 수 있다!
Clock 객체를 mocking 하기 위해서는 결국 호출하는 프로젝션 코드를 변경할 수 밖에 없습니다.
하지만, Oracle에서 테스트를 위해 Clock을 받아 사용하는 목적으로 사용하라고 명시되어있어서 쿨하게 넘어가기로 했습니다.
This will query the specified clock to obtain the current date - today. Using this method allows the use of an alternate clock for testing. The alternate clock may be introduced using dependency injection.
이렇게 정적 메소드인 LocalDate.now() 메소드를 mocking 하는 방법에 대해서 알아보았습니다.
더 좋은 방법과 질문이 있으시다면 댓글 언제나 환영이고
✨ 긴글 읽어주셔서 감사합니다! ✨