Mockito 사용중 Unnecessary Stubbing Exception 해소하기
Spring Boot에서 JUnit5와 이에 포함된 Mockito Core 3.x 버전을 사용할 때 아래와 같이 UnnecessaryStubbingException
에러가 나면서 테스트코드가 실패하는 경우가 있습니다.
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
이는 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) 을 참고하기 바랍니다.
Mockito-core 3.x버전에서는 위에서 설명한 엄격성이 강제되었기 때문에, 불필요한 stubbing 코드를 제거해야 합니다. 해결방법은 3가지가 있습니다.
1. Mockito-core 버전을 1.x나 2.x로 내리기
- 최악의 해법이지만, 수정되어야 하는 코드가 많을 경우 어쩔 수 없이 선택해야하는 경우의 수입니다.
- 2.x 에서는 엄격성 개념이 도입되었지만, 테스트에 아래와 같이 MockitoSettings를 지정해주면 엄격성을 우회해서 테스트할 수 있습니다.
@MockitoSettings(strictness = Strictness.WARN)
@Test
void foo() throws Exception {
// Test blah blah...
}
2. lenient() 메서드를 앞에 추가하기
doReturn
,when
등의 앞에lenient()
를 추가해서 해당 stubbing이 미사용될 수 있음을 표시합니다.- 일반적으로,
@BeforeEach
처럼 전체 테스트에 적용되어야 하는 stubbing이 일부 엣지 케이스에서 미사용될때 사용하는 것이 좋습니다.- 남발하게 된다면, 과한 코드의 중복을 보게 되어 Mockito에서 추구하고자 한 테스트의 개선에 도움이 되지 않습니다.
// lenient()를 붙여서 mocking 결과를 항상 사용하진 않음을 표시합니다.
lenient().doReturn(fixedClock.instant()).when(clock).instant();
lenient().doReturn(fixedClock.getZone()).when(clock).getZone();
3. 필요없는 stubbing을 제거하기
- 말 그대로 필요없는
when
,doReturn
,doThrow
등을 제거합니다. - 불필요한 테스트가 줄어들고, 중복 mocking이 줄어들기 때문에 나중에 코드를 수정하는 사람이 테스트코드를 이해하는데 편해집니다.