You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JUnit4/Mockito验证方法内异常的抛出与捕获?

用JUnit4+Mockito验证方法内部捕获的异常

先说明白:因为这个异常被方法自己的try/catch抓了处理了,外部没法直接用JUnit的@Test(expected = ...)或者Mockito的验证工具直接查到这个异常,但可以用间接方式验证异常确实被抛出并处理了——核心是触发异常场景,然后验证方法的行为符合预期,同时确保触发条件到位。

具体怎么做?

你的示例里,someMethod的返回值决定了Integer.parseInt会不会抛NumberFormatException,所以我们只要控制someMethod返回一个非数字的字符串,就能触发异常,然后看方法是不是返回了默认值,间接证明异常被抓了。

分两种情况来看:

情况1:someMethod是外部依赖的方法

如果someMethod是另一个注入的服务提供的,直接mock这个依赖就行:

// 被测类
public class MyService {
    private final SomeDependency dependency;

    public MyService(SomeDependency dependency) {
        this.dependency = dependency;
    }

    public int someMethodAsInt(String key, int defaultValue) {
        try {
            return Integer.parseInt(dependency.someMethod(key, String.valueOf(defaultValue)));
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
}

// 依赖的接口
public interface SomeDependency {
    String someMethod(String key, String defaultValue);
}

测试代码这么写:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock
    private SomeDependency mockDependency;

    @Test
    public void someMethodAsInt_ThrowsNumberFormatException_ReturnsDefault() {
        // 让mock的依赖返回非数字字符串,逼Integer.parseInt抛异常
        when(mockDependency.someMethod("key", "123")).thenReturn("not-a-number");

        MyService myService = new MyService(mockDependency);
        int result = myService.someMethodAsInt("key", 123);

        // 验证方法返回默认值,说明异常被抓了处理了
        assertEquals(123, result);
        // 还可以加一句验证mock方法确实被调用了,确保触发异常的条件真的满足
        // Mockito.verify(mockDependency).someMethod("key", "123");
    }
}

情况2:someMethod是被测类自己的内部方法

如果someMethod是MyService自己的方法,就用Mockito的spy来部分mock这个对象,让内部的someMethod返回触发异常的字符串:

// 被测类
public class MyService {
    public String someMethod(String key, String defaultValue) {
        // 原来的逻辑,比如返回defaultValue
        return defaultValue;
    }

    public int someMethodAsInt(String key, int defaultValue) {
        try {
            return Integer.parseInt(someMethod(key, String.valueOf(defaultValue)));
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
}

测试代码:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doReturn;

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Spy
    private MyService myServiceSpy;

    @Test
    public void someMethodAsInt_ThrowsNumberFormatException_ReturnsDefault() {
        // 部分mock内部的someMethod,返回非数字字符串
        doReturn("not-a-number").when(myServiceSpy).someMethod("key", "123");

        int result = myServiceSpy.someMethodAsInt("key", 123);

        // 验证返回默认值,证明异常被捕获处理了
        assertEquals(123, result);
    }
}

重要提醒

  1. 没法直接验证异常抛出:JUnit和Mockito原生没提供办法直接检测方法内部被捕获的异常(除非用PowerMock这类扩展,但你说不能用),所以只能靠间接验证。
  2. 单元测试看行为不看细节:其实单元测试的核心是验证方法的行为是否符合预期,而不是纠结内部有没有抛异常。只要异常场景下方法返回了正确的默认值,就已经达到测试目的了,没必要硬要验证“异常确实被抛过”这个内部细节。

内容的提问来源于stack exchange,提问作者r17

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:53:24