如何用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); } }
重要提醒
- 没法直接验证异常抛出:JUnit和Mockito原生没提供办法直接检测方法内部被捕获的异常(除非用PowerMock这类扩展,但你说不能用),所以只能靠间接验证。
- 单元测试看行为不看细节:其实单元测试的核心是验证方法的行为是否符合预期,而不是纠结内部有没有抛异常。只要异常场景下方法返回了正确的默认值,就已经达到测试目的了,没必要硬要验证“异常确实被抛过”这个内部细节。
内容的提问来源于stack exchange,提问作者r17
相关产品推荐
相关产品推荐

