안녕하세요. 개발자 Jindory입니다.
오늘은 Junit Test시 사용하는 Mockito 사용중 Unnecessary Stubbing Exception 발생시 해결하는 방법에 대해서 작성해보고자 합니다.
# 글 작성 이유
Junit 테스트를 하다가 org.mockito.exceptions.misusing.UnnecessaryStubbingException이 발생하여 해당 에러의 내용이 무엇이며, 어떻게 해결할 수 있는지 기록하고자 글을 작성하게 되었습니다.
# Unnecessary Stubbing Exception이 무엇인가?
이는 mockito-core 버전이 1.x일 때 없었던 Strictness(테스트코드의 엄격성)을 규정하기 위해 생긴 에러이며, mockito-core 2.x버전에서 도입되었습니다.
테스트코드의 엄격성은 다음 요소들을 보장해 줍니다.
- 테스트코드에서 사용되지 않는 stub(when/thenReturn)을 줄여줍니다.
- 테스트코드에서 불필요한 코드 중복을 없애주고 이를 통해 필요없는 테스트코드 역시 줄여줍니다.
- 죽은 코드를 제거하면서 생기는 불필요한 테스트를 없애도록 도와줍니다.
- 이를 통해 디버깅 편의성과 생산 효율을 올려줍니다.
테스트 코드의 엄격성도입은 mockito의 아래의 철학과 관련이 있습니다.
Use the right tools and get cleaner tests, faster
도입을 결정하게 된 깃헙 이슈와 해당 이슈에 레퍼런스된 Mockito 개발자의 철학에 대한 설명 , 그리고 stubbing 감지의 장점을 좀 더 디테일하게 설명한 Mockito 블로그 글(Clean tests produce clean code - strict stubbing in Mockito) 을 참고하기 바랍니다.
# Unnecessary Stubbing Exception 해결방법
1. Lenient() 메서드를 앞에 추가하기
- doReturn, when 등의 앞에 lenient()를 추가해서 해당 stubbing이 미사용될 수 있음을 표시합니다.
- 일반적으로 @BeforeEach처럼 전체 테스트에 적용되어야 하는 stubbing이 일부 엣지 케이스에서 미사용될때 사용하는것이 좋습니다.
※ 남발하게 된다면, 과한 코드의 중복을 보게 되어 Mocito에서 추구하고자 한 테스트의 개선에 도움이 되지 않습니다.
2. 필요없는 Sttubbing 제거하기
- 말 그대로 필요없는 when, doReturn, doThrow 등을 제거합니다.
- 불필요한 테스트가 줄어들고, 중복 mocking이 줄어들기 때문에 나중에 코드를 수정하는 사람이 테스트코드를 이애하는데 편해집니다.
3. Mockito-core버전을 1.x나 2.x로 내리기
- 마지막으로 최악의 해법이지만, 수정되어야 하는 코드가 많을 경우 어쩔 수 없이 선택해야하는 경우의 수입니다.
- 2.x에서는 엄격성 개념이 도입되었지만, 테스트에 아래와 같이 MockitoSettings를 지정해주면 엄격성을 우회해서 테스트를 할 수 있습니다.
@MockitoSettings(strictness = Strictness.WARN)
@Test
void foo() throws Exception {
// Test blah blah...
}
# Unnecessary Stubbing Exception 해결하기
저 같은 경우는 save Method 호출이 조회한 결과가 존재하는 경우에만 실행되어야 하는데 이를 염두하지 못하고 save 기능을 테스트하는 코드를 작성하여 위와 같은 문제가 발생했다고 생각합니다.
그래서 기존 코드를 아래와 같이 작성하여 해결했습니다.
* Service Layer
* * *
JoinEntity approval = inquiryList.stream()
.filter(d->d.getPlayerId() == joinDto.getPlayerId())
.map(d->d.toEntity(d,player,team))
.findFirst().orElseThrow(()-> new Exception("해당 선수가 "+StatusType.APPROVAL+"한 요청이 없습니다."));
// 만일 승인된 Request가 존재한다면
if(approval!=null) {
approval.updateStatus(StatusType.CONFIRMATION);
// 승인 확정 후 해당 선수를 팀으로 영입하여 선수 명단에 선수의 정보를 추가한다.
// 승인 확정 후 해당 선수는 소속팀과 유니폼 번호를 변경한다.
List<PlayerEntity> playerList = playerRepositoryCustom.findPlayer(null, null, team.getTeamName(), page);
Integer playerMaxNo = playerList.stream()
.sorted(Comparator.comparing(PlayerEntity::getUniformNo,Comparator.reverseOrder()))
.map(PlayerEntity::getUniformNo).toList().get(0);
// 유니폼 번호는 소속팀의 가장 큰 유니폼 번호보다 1 큰 숫자를 설정한다.
player.updateInfo(player.getPlayerName(), player.getResRegNo(), playerMaxNo+1, team);
return approval.toDto();
}else {
return null;
}
1-1) 기존 Test 코드
approveJoinEntity.updateStatus(StatusType.CONFIRMATION);
when(joinRepository.save(any())).thenReturn(approveJoinEntity);
1-2) 발생한 에러
2-1) 변경한 Test 코드
approveJoinEntity.updateStatus(StatusType.CONFIRMATION);
lenient().doReturn(approveJoinEntity).when(joinRepository).save(any());
2-2) 테스트 결과
이렇게 Junit Test시 사용하는 Mockito 사용중 Unnecessary Stubbing Exception 발생시 해결하는 방법에 대해서 알아봤습니다.
혹시라도 정정할 내용이나 추가적으로 필요하신 정보가 있다면 댓글 남겨주시면 감사하겠습니다.
오늘도 Jindory 블로그에 방문해주셔서 감사합니다.
[ 참고 ]
'트러블슈팅 > Java' 카테고리의 다른 글
[SpringBoot] HttpRequestMethod Not Supported Exception 해결방법 (0) | 2022.03.12 |
---|---|
[Java] Java Spring UTF8 설정 오류(한글 깨짐) 해결방법 (0) | 2022.03.06 |