如何在测试类中验证内部抛出的异常?
问题:如何断言被捕获的内部异常确实抛出?
我在测试类A,它会调用类B的methodB方法。B的methodB内部会抛出Exception,但该异常被catch块捕获后仅通过Logger打印日志。我需要断言这个内部异常确实被抛出,但当前测试代码报错。
相关代码
Class A
public class A { private static Logger logger; private B b; public A() { logger = LoggerFactory.getLogger("A"); b = new B(); } public void methodA() { b.methodB(); logger.info("A"); } }
Class B
public class B { private static Logger logger; public B() { logger = LoggerFactory.getLogger("B"); } public void methodB() { try { throw new Exception("NULL"); } catch(Exception e) { logger.info("Exception thrown"); } } }
测试代码
@Test public void testException() { A a = new A(); a.methodA(); assertThrows(Exception.class, () -> b.methodB()); }
遇到的错误
Expected java.lang.Exception to be thrown, but nothing was thrown.
org.opentest4j.AssertionFailedError: Expected java.lang.Exception to be thrown, but nothing was thrown.
解决方案
因为methodB内部已经捕获了异常,不会将异常抛出到方法外部,所以assertThrows无法直接检测到。你可以通过以下几种方式验证:
方法1:Mock Logger,验证日志输出
使用Mockito等工具mock B类的Logger,验证是否打印了对应的异常日志,间接证明异常被抛出并捕获。
示例测试代码:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.slf4j.Logger; import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class ATest { @Mock private Logger bLogger; @Test public void testExceptionThrownAndCaught() { // 替换B类的静态Logger为mock对象 B.logger = bLogger; A a = new A(); a.methodA(); // 验证Logger是否打印了指定日志 verify(bLogger).info("Exception thrown"); } }
方法2:使用PowerMock拦截异常抛出(不推荐修改代码时用)
如果无法修改原代码,可使用PowerMock拦截methodB内部的异常抛出动作,直接验证异常是否被抛出。
示例测试代码:
import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest(B.class) public class ATest { @Test(expected = Exception.class) public void testExceptionThrownInternally() throws Exception { // 部分mock B类,让methodB直接抛出异常 B mockB = PowerMockito.spy(new B()); PowerMockito.doThrow(new Exception("NULL")).when(mockB).methodB(); // 替换A中的b实例为mock对象 A a = new A(); PowerMockito.field(A.class, "b").set(a, mockB); a.methodA(); } }
方法3:修改代码设计(推荐)
如果允许调整原代码,可以让methodB暴露捕获的异常,让测试更直接:
修改Class B:
public class B { private static Logger logger; private Exception caughtException; public B() { logger = LoggerFactory.getLogger("B"); } public void methodB() { try { throw new Exception("NULL"); } catch(Exception e) { caughtException = e; logger.info("Exception thrown"); } } // 新增方法供测试获取捕获的异常 public Exception getCaughtException() { return caughtException; } }
测试代码:
import org.junit.Test; import org.powermock.reflect.Whitebox; import static org.junit.Assert.*; public class ATest { @Test public void testExceptionThrown() { A a = new A(); a.methodA(); // 获取A中的b实例,验证捕获的异常 B b = Whitebox.getInternalState(a, "b"); assertNotNull(b.getCaughtException()); assertEquals("NULL", b.getCaughtException().getMessage()); } }
内容的提问来源于stack exchange,提问作者adityaViswanatham
相关产品推荐
相关产品推荐

