1. 문제 정의
Mockito로 단위 테스트를 작성하다 보면, void 반환 타입의 메서드가 특정 상황에서 예외(exception)를 던지는지 검증하고 싶은 경우가 있습니다. 예를 들어 저장 실패 시 RepositoryException을 던져야 하는 서비스 메서드를 테스트한다고 가정해 봅시다.
| |
이때 repository.insert(...)가 예외를 던지도록 Mockito로 스텁(stub)을 만들고자 doThrow()를 사용하다가, 많은 개발자들이 아래 형태로 작성해서 컴파일 오류를 만납니다.
| |
실행시키면 다음과 같은 컴파일 오류가 발생합니다.
| |
이 글은 이 오류가 왜 일어나는지, 정확히 어떻게 고쳐야 하는지를 짧은 코드 재현을 통해 설명합니다.
2. 원인 탐구
왜 “arguments (void)” 오류가 나올까
Mockito.when(T) 메서드의 시그니처는 다음과 같습니다.
| |
즉, when() 괄호 안에는 어떤 객체(for)가 들어와야 합니다. 그런데 잘못된 호출에서는 괄호 안에 mockedObject.methodReturningVoid(...)라는 메서드 호출식을 넣었습니다.
methodReturningVoid()의 반환 타입이 void라면, 이 호출식 자체는 아무런 값도 반환하지 않습니다. 곧 when() 의 파라미터로 전달된 것은 void 값을, 없음을 넘긴 셈이 되고, 컴파일러는 다음과 같은 불평을 합니다.
| |
즉 “너가 when()에 넘긴 것은 void 값이다. T 자리에 void 는 맞지 않는다"는 뜻의 오류였습니다.
void 메서드와 일반 메서드의 차이
반환 값이 있는 메서드는 when(mock.method()).thenReturn(...)처럼 when() 안에 메서드 호출이 잘 들어갑니다. 이유는 값을 반환하기 때문입니다.
String getName()같은 메서드 →when(mock.getName()).thenReturn("kim")가능void insert(...)같은 메서드 →when(mock.insert(...))불가능, void 값이라서
3. 근본 원인 분석
이 오류의 본질은 Mockito 문법을 잘못 사용한 것이 아니라, 괄호(파렌시스)의 위치입니다.
Mockito 공식 문서의 doThrow()/doAnswer() 구문은 아래와 같은 형태입니다.
| |
구조를 나워 보면:
doThrow(...)— 발생시킬 예외를 지정.when(mockedObject)— mock 객체를 지정 (괄호 안에 mock 인스턴스를 넣는다).someVoidMethod()— void 메서드를 호출
즉, when() 괄호 안에는 mock 객체를 넣고, void 메서드 호출은 when() 뒤에 체이닝으로 이어 붙입니다.
반면 실패한 코드에서는 mock과 methodCall()을 한 통로에 묶어서 when() 안에 넣으니 void 값이 인자로 전달된 것입니다.
| 비교 대상 | 올바른 코드 | 잘못된 코드 |
|---|---|---|
| 문법 형태 | doThrow(...).when(mock).voidMethod() | doThrow(...).when(mock.voidMethod()) |
| when() 인자 | mock 객체 (인스턴스) | void 메서드 호출 결과 (값 없음) |
| 결과 | 정상 동작 | 컴파일 오류 when(T) not applicable for (void) |
이 표가 [올바른 괄호 위치]를 한눈에 정리해 줍니다.
4. 코드 해결책
최소 재현 코드
Mockito + JUnit에서 void 메서드가 예외를 던지게 하고 싶다면 아래처럼 작성하면 됩니다.
| |
중요한 부분을 다시 한번 강조합니다.
| |
5. 향후 예방 조치
실전 적용 체크
- 로컬:
when()인자에 mock 인스턴스가 들어가는지, 아니면 메서드 호출이 들어가는지 찾아보세요.void호출이 인자에 들어가면 이 오류가 납니다. - CI: Mockito 버전이 오래되었다면 (2.x대 초반)
doThrow().when()동작이 일부 다를 수 있습니다. 버전에 따라 “환경에 따라 다를 수 있으므로” 목 크게 최신 3.x/5.x를 권장합니다. - 테스트 의도: 무슨 예외가 던져지는지를 검증할 때는
@Test(expected = ...)보다 JUnit 5의assertThrows가 더 권장됩니다. (검증 명령 섹션에서 함께 정리)
검증 명령
의존성이 제대로 잡혀 있고 테스트가 통과하는지 확인합니다 (Gradle 기준):
| |
또는 Maven이라면:
| |
만약 수정 후에도 오류가 계속 나온다면, 다시 괄호 위치를 아래와 같이 대입해서 확인하세요.
| |
자주 하는 실수(잘못된 해결책)
doThrow().when(mock)대신mock.method()전체를 when 안에 넣기 — 이것이 이번 오류의 직접적 원인입니다. 절대when(mock.voidMethod())로 작성하지 마세요.- 예외를
new Exception()대신 Checked 예외 테스트에 억지로 맞추기 — 테스트가 컴파일 오류를 숨기기 위해 의미없는 try/catch 추가하는 것은 지양; 필요한 예외 타입만 지정합니다. 이는 “일반적인 테스트 작전” 기준이며 환경에 따라 다를 수 있습니다.
6. DevTrace verdict
이 오류의 핵심은 “Mockito가 예외를 못 던지게 하는 게 아니라,
when()괄호 안에 void 메서드 호출 결과(값이 없음)를 넘기는 문법 오류"라는 점입니다. 즉 코드를 삭제하고 다시 쓰기보다, doThrow(…).when(mock).method() 처럼 괄호 위치만 바로잡으면 그 즉시 해결됩니다.
주출처
- Mockito 공식 문서 (Stubbing 개념): https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#17
- 원본 질문/답변: Stack Overflow — Mockito test a void method throws an exception
본 문서의 에러 로그와 해결책은 위 Stack Overflow 질문(채택 답변)을 근거로 합니다. Mockito 버전별 동작 차이는 사용 환경에 따라 검증이 필요할 수 있습니다.
DevTrace 결론
Mockito에서 void 메서드의 예외 스텁을 만들 때는 중괄호가 아니라 괄호 위치가 핵심이며, doThrow(…).when(mock).method() 구문에서 when() 안에는 mock 객체만 배치해야 컴파일 오류를 피할 수 있다.
원문 출처는 문제 발견의 단서이며, 위 판단과 점검 항목은 DevTrace의 독자 분석이다.