Service 계층 - Static 클래스 테스트
Static 클래스 mocking하기
MockedStatic
mockito docs : Interface MockedStatic
Mocking은 이 정적 모의가 생성된 스레드에만 영향을 미치며 다른 스레드에서 이 개체를
사용하는 것은 안전하지 않다. 따라서 close()
메소드를 호출해서 자원을 해제해줘야 한다.
그렇지 않으면 정적 모의 객체가 활성상태로 유지된다.
따라서 Junit을 사용할 때 try-with-resources 문 내에서 이 객체를 생성하는 것이 좋다.
구현하기
stackoverflow에 올라온 질문이다. 그리고 답변에 다양한 구현방법이 적혀있다.
[ @BeforeAll, @AfterAll ]
private static MockedStatic<JwtUtil> jwtUtil;
@BeforeAll
public static void mockJwtUtil() {
jwtUtil = mockStatic(JwtUtil.class);
}
@AfterAll
public static void mockJwtUtilEnd() {
jwtUtil.close();
}
[ try-with-resources ]
여기에 관련된 설명이 잘되어있다.
try (MockedStatic<JwtUtil> mockedStatic = mockStatic(JwtUtil.class)) {
given(JwtUtil.agentId()).willReturn(1L);
}
자동으로 close();
메소드를 호출해준다. 그러면 어떻게 자동으로 호출해줄까?🤔
그 이유는 바로 AutoCloseable 인터페이스 덕분이다.
위 다이어크램 처럼 MocokedStatic 인터페이스는 최상위에 AutoClosable 인터페이스가 있다.
AutoCloseable 인터페이스
AutoCloseable 인터페이스에 대해 알아보고 넘어가자.
[ 설명 ]
해당 자원 할당을 해제한다. 이 메서드는 try-with-resources 문에 의해 관리되는 객체에서 자동으로
호출된다. 이 인터페이스 메서드는 예외를 throw하도록 선언되지만 개발자는 close()
메서드의
구체적인 구현을 선언하여 보다 구체적인 예외를 throw하거나 닫기 작업이 실패할 수 없는 경우 예외를
전혀 throw하지 않는 것이 좋다.
그리고 java.io.Closeable#close()
메소드와 달리 이 close()
메소드는 멱등성이 필요하지 않다.
즉, 이 close()
메서드를 두 번 이상 호출하면 효과가 없어야 하는 Closeable.close와 달리 눈에 띄는
부작용이 발생할 수 있다. 따라서 개발자는 이 인터페이스의 close()
메소드를 멱등성으로 만드는 것이 좋다.
테스트 코드 작성
최초 이적 제안하기.
private static MockedStatic<JwtUtil> jwtUtil;
@BeforeAll
public static void mockJwtUtil() {
jwtUtil = mockStatic(JwtUtil.class);
}
@AfterAll
public static void mockJwtUtilEnd() {
jwtUtil.close();
}
@ExtendsWith(MockitoExtension.class)
class AgentServiceImplTest {
@Test
@DisplayName("최초 이적 제안하기")
void offerTransfer() throws Exception {
Player player = new Player(1L, agent, team, position, "메시", 34);
Contract contractCond = Contract.builder()
.contractId(1L).payment(1000L).transferFee(2000L)
.contractDivision(LOAN).contractYear(1).contractMonth(6)
.currencyUnit(EURO).build();
Offer offer = Offer.builder()
.offerId(1L).agent(agent).player(player)
.contract(contractCond).contractStatus(NEGOTIATING)
.build();
Long playerId = 1L;
//given
given(JwtUtil.agentId()).willReturn(1L);
given(agentRepository.findById(any())).willReturn(ofNullable(agent));
given(playerRepository.findById(any())).willReturn(of(player));
given(offerRepository.findPreviousOffer(anyLong(), anyLong())).willReturn(empty());
given(offerRepository.save(any())).willReturn(offer);
//when
Offer offerTransfer = agentService.offerTransfer(1L, playerId, contractCond);
//then
assertThat(offer.getPlayer().getName()).isEqualTo(offerTransfer.getPlayer().getName());
}
}