1. 概述

在本篇教程中,我们聚焦于如何使用 Mockito 来配置方法调用时抛出异常。

如果你对 Mockito 还不熟悉,可以查看我们的 Mockito 系列文章

下面是我们将使用的一个简单字典类:

class MyDictionary {
    
    private Map<String, String> wordMap;

    public void add(String word, String meaning) {
        wordMap.put(word, meaning);
    }

    public String getMeaning(String word) {
        return wordMap.get(word);
    }
}

2. 非 void 返回类型的方法

✅ 如果方法有返回值(非 void),我们可以使用 when().thenThrow() 的方式来模拟异常:

@Test
void givenNonVoidReturnType_whenUsingWhenThen_thenExceptionIsThrown() {
    MyDictionary dictMock = mock(MyDictionary.class);
    when(dictMock.getMeaning(anyString())).thenThrow(NullPointerException.class);
    
    assertThrows(NullPointerException.class, () -> dictMock.getMeaning("word"));
}

这段代码中,我们配置了 getMeaning() 方法在被调用时会抛出 NullPointerException。由于该方法返回的是 String 类型,因此可以直接使用 when().thenThrow() 结构。

3. 返回类型为 void 的方法

⚠️ 如果方法没有返回值(即 void),就不能使用 when().thenThrow(),因为 Java 编译器不允许将 void 方法写在括号表达式中。

此时应改用 doThrow().when() 的语法结构:

@Test
void givenVoidReturnType_whenUsingDoThrow_thenExceptionIsThrown() {
    MyDictionary dictMock = mock(MyDictionary.class);
    doThrow(IllegalStateException.class).when(dictMock)
        .add(anyString(), anyString());
    
    assertThrows(IllegalStateException.class, () -> dictMock.add("word", "meaning"));
}

上面的例子中,我们让 add() 方法在调用时抛出 IllegalStateException

4. 以对象形式传递异常

除了传入异常类之外,你也可以直接传入一个异常实例:

@Test
void givenNonVoidReturnType_whenUsingWhenThenAndExeceptionAsNewObject_thenExceptionIsThrown() {
    MyDictionary dictMock = mock(MyDictionary.class);
    when(dictMock.getMeaning(anyString())).thenThrow(new NullPointerException("Error occurred"));
    
    assertThrows(NullPointerException.class, () -> dictMock.getMeaning("word"));
}

同样的方式也适用于 doThrow()

@Test
void givenNonVoidReturnType_whenUsingDoThrowAndExeceptionAsNewObject_thenExceptionIsThrown() {
    MyDictionary dictMock = mock(MyDictionary.class);
    doThrow(new IllegalStateException("Error occurred")).when(dictMock)
        .add(anyString(), anyString());
    
    assertThrows(IllegalStateException.class, () -> dictMock.add("word", "meaning"));
}

这种方式可以方便地自定义异常信息,非常适合调试和测试场景。

5. Spy 对象的异常模拟

✅ 使用 spy 时,也可以像 mock 一样配置其抛出异常的行为:

@Test
void givenSpyAndNonVoidReturnType_whenUsingWhenThen_thenExceptionIsThrown() {
    MyDictionary dict = new MyDictionary();
    MyDictionary spy = Mockito.spy(dict);
    when(spy.getMeaning(anyString())).thenThrow(NullPointerException.class);
    
    assertThrows(NullPointerException.class, () -> spy.getMeaning("word"));
}

注意:虽然 spy 是真实对象的代理,但在测试中仍然可以通过 when().thenThrow() 控制其行为。

6. 小结

在这篇文章中,我们学习了如何通过 Mockito 配置方法调用来抛出异常,包括:

  • ✅ 非 void 方法使用 when().thenThrow()
  • ⚠️ void 方法使用 doThrow().when()
  • ✅ 可以传入异常类或异常实例
  • ✅ spy 对象同样支持异常模拟

完整代码可以在 GitHub 上找到。


原始标题:Mocking Exception Throwing using Mockito