1. 문제 정의

Mockito로 단위 테스트를 작성하다 보면, void 반환 타입의 메서드가 특정 상황에서 예외(exception)를 던지는지 검증하고 싶은 경우가 있습니다. 예를 들어 저장 실패 시 RepositoryException을 던져야 하는 서비스 메서드를 테스트한다고 가정해 봅시다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class UserService {
    private final UserRepository repository;

    public UserService(UserRepository repository) {
        this.repository = repository;
    }

    public void save(User user) {
        repository.insert(user);   // 내부적으로 무언가 실패하면 exception 가능
    }
}

이때 repository.insert(...)가 예외를 던지도록 Mockito로 스텁(stub)을 만들고자 doThrow()를 사용하다가, 많은 개발자들이 아래 형태로 작성해서 컴파일 오류를 만납니다.

1
doThrow(new Exception()).when(mockedObject.methodReturningVoid(...));  // ❌ 컴파일 오류

실행시키면 다음과 같은 컴파일 오류가 발생합니다.

1
The method when(T) in the type Stubber is not applicable for the arguments (void)

이 글은 이 오류가 왜 일어나는지, 정확히 어떻게 고쳐야 하는지를 짧은 코드 재현을 통해 설명합니다.

2. 원인 탐구

왜 “arguments (void)” 오류가 나올까

Mockito.when(T) 메서드의 시그니처는 다음과 같습니다.

1
public static <T> OngoingStubbing<T> when(T methodCall);

즉, when() 괄호 안에는 어떤 객체(for)가 들어와야 합니다. 그런데 잘못된 호출에서는 괄호 안에 mockedObject.methodReturningVoid(...)라는 메서드 호출식을 넣었습니다.

methodReturningVoid()의 반환 타입이 void라면, 이 호출식 자체는 아무런 값도 반환하지 않습니다. 곧 when() 의 파라미터로 전달된 것은 void 값을, 없음을 넘긴 셈이 되고, 컴파일러는 다음과 같은 불평을 합니다.

1
The method when(T) in the type Stubber is not applicable for the arguments (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() 구문은 아래와 같은 형태입니다.

1
doThrow(new RuntimeException()).when(mockedObject).someVoidMethod();

구조를 나워 보면:

  1. doThrow(...) — 발생시킬 예외를 지정
  2. .when(mockedObject) — mock 객체를 지정 (괄호 안에 mock 인스턴스를 넣는다)
  3. .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 메서드가 예외를 던지게 하고 싶다면 아래처럼 작성하면 됩니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import org.junit.Test;

public class UserServiceTest {

    @Test(expected = Exception.class)
    public void shouldThrowWhenRepositoryFails() throws Exception {
        // mock 준비
        UserRepository repository = mock(UserRepository.class);
        UserService service = new UserService(repository);

        // doThrow(...).when(mock).voidMethod()  ← 올바른 괄호 위치
        doThrow(new Exception()).when(repository).insert(any(User.class));

        // 예외가 발생해야 하는 동작 호출
        service.createUser(new User());
    }
}

중요한 부분을 다시 한번 강조합니다.

1
2
3
doThrow(new Exception()).when(mockedObject).methodReturningVoid(...);
//                      ↑ 여기서 괄호를 닫고 mock 객체만 넣는다
//                                       ↑ void 메서드는 when() 뒤에 이어 붙인다

5. 향후 예방 조치

실전 적용 체크

  • 로컬: when() 인자에 mock 인스턴스가 들어가는지, 아니면 메서드 호출이 들어가는지 찾아보세요. void 호출이 인자에 들어가면 이 오류가 납니다.
  • CI: Mockito 버전이 오래되었다면 (2.x대 초반) doThrow().when() 동작이 일부 다를 수 있습니다. 버전에 따라 “환경에 따라 다를 수 있으므로” 목 크게 최신 3.x/5.x를 권장합니다.
  • 테스트 의도: 무슨 예외가 던져지는지를 검증할 때는 @Test(expected = ...)보다 JUnit 5의 assertThrows가 더 권장됩니다. (검증 명령 섹션에서 함께 정리)

검증 명령

의존성이 제대로 잡혀 있고 테스트가 통과하는지 확인합니다 (Gradle 기준):

1
./gradlew test --tests "UserServiceTest"

또는 Maven이라면:

1
mvn -Dtest=UserServiceTest test

만약 수정 후에도 오류가 계속 나온다면, 다시 괄호 위치를 아래와 같이 대입해서 확인하세요.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 컴파일 오류 발생 여부 확인을 위해 예상되는 검증 (예시)

## 검증 환경

본 절은 원문 사례·문서에 등장한 버전/도구를 정리한 것이다. 별도 실험실 재현이 명시되지 않은 항목은 일반화하지 않는다.

| 항목 | 본문·원문에서 확인된 범위 |
|------|---------------------------|
| 런타임/도구 | JUnit 5 |
| 문서 정리일 | 2026-09-02 |
- OS/CI 세부 값은 프로젝트마다 다르므로, 적용 전 로컬에서 동일 오류 메시지를 재확인한다.

./gradlew compileTestJava

자주 하는 실수(잘못된 해결책)

  • doThrow().when(mock) 대신 mock.method() 전체를 when 안에 넣기 — 이것이 이번 오류의 직접적 원인입니다. 절대 when(mock.voidMethod())로 작성하지 마세요.
  • 예외를 new Exception() 대신 Checked 예외 테스트에 억지로 맞추기 — 테스트가 컴파일 오류를 숨기기 위해 의미없는 try/catch 추가하는 것은 지양; 필요한 예외 타입만 지정합니다. 이는 “일반적인 테스트 작전” 기준이며 환경에 따라 다를 수 있습니다.

6. DevTrace verdict

이 오류의 핵심은 “Mockito가 예외를 못 던지게 하는 게 아니라, when() 괄호 안에 void 메서드 호출 결과(값이 없음)를 넘기는 문법 오류"라는 점입니다. 즉 코드를 삭제하고 다시 쓰기보다, doThrow(…).when(mock).method() 처럼 괄호 위치만 바로잡으면 그 즉시 해결됩니다.

주출처

본 문서의 에러 로그와 해결책은 위 Stack Overflow 질문(채택 답변)을 근거로 합니다. Mockito 버전별 동작 차이는 사용 환경에 따라 검증이 필요할 수 있습니다.

DevTrace 결론

Mockito에서 void 메서드의 예외 스텁을 만들 때는 중괄호가 아니라 괄호 위치가 핵심이며, doThrow(…).when(mock).method() 구문에서 when() 안에는 mock 객체만 배치해야 컴파일 오류를 피할 수 있다.

원문 출처는 문제 발견의 단서이며, 위 판단과 점검 항목은 DevTrace의 독자 분석이다.