如何用JUnit的@Test(expected = SQLException.class)覆盖方法的catch代码块?
getId方法的catch代码块(及为什么@Test(expected = SQLException.class)直接用不了) 嘿,我来帮你理清楚这个问题~首先得明确一个核心点:你的getId方法并没有把捕获到的SQLException重新抛出——它只是在catch块里打了日志就结束了。所以直接用@Test(expected = SQLException.class)是不会生效的,因为异常已经被方法内部“吞掉”了,测试方法根本接收不到这个异常。
那怎么才能覆盖到catch代码块呢?我们需要用Mock框架(比如Mockito)模拟JDBC操作抛出SQLException,然后验证catch块里的逻辑是否正确执行。下面是具体的步骤和示例:
步骤1:明确测试目标
我们要验证的是:当JDBC操作抛出SQLException时,方法会记录指定的错误日志,并且返回默认值0。
步骤2:用Mockito编写测试用例
假设你的类名为ManagerDao,我们需要Mock它的DataSource依赖,模拟获取连接或执行SQL时抛出异常:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.slf4j.Logger; import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class ManagerDaoTest { // Mock数据源,模拟JDBC操作 @Mock private DataSource dataSource; @Mock private Connection connection; @Mock private PreparedStatement preparedStatement; // Mock日志对象,用于验证是否输出了预期日志 @Mock private Logger logger; // 注入Mock对象到目标测试类 @InjectMocks private ManagerDao managerDao; @Test public void getId_WhenSQLExceptionOccurs_ShouldLogErrorAndReturnZero() throws SQLException { // 1. 模拟数据源获取连接时抛出SQLException when(dataSource.getConnection()).thenThrow(new SQLException("Test SQL Exception")); // 2. 执行目标方法 int result = managerDao.getId("testCol1", 123, "testCol3"); // 3. 验证方法是否返回默认值0 assertEquals(0, result); // 4. 验证catch块中的日志是否被正确记录 verify(logger).error("ERROR-WARNING:<<<getId():: ID testCol1 SQLException occurred while selecting data table."); } }
为什么不能直接用@Test(expected = SQLException.class)?
因为你的getId方法在catch块中捕获了SQLException,没有重新抛出它(也没有包装成其他异常抛出)。这意味着异常在方法内部就被处理了,不会传播到测试方法中,所以@Test(expected)会因为没捕获到预期异常而失败。
如果一定要用@Test(expected)的方式,你需要修改原方法,让它重新抛出异常,比如:
catch (SQLException e) { logger.error("ERROR-WARNING:<<<getId():: ID "+col1+" SQLException occurred while selecting data table."); throw e; // 重新抛出SQLException }
或者包装成自定义异常抛出:
catch (SQLException e) { logger.error("ERROR-WARNING:<<<getId():: ID "+col1+" SQLException occurred while selecting data table."); throw new DatabaseDownException("Database error", e); // 假设DatabaseDownException是你的自定义异常 }
但这样修改需要评估是否符合你的业务逻辑,毕竟原方法设计是吞掉异常返回默认值。
补充:如果日志是静态获取的怎么办?
如果你的日志是通过LoggerFactory.getLogger(ManagerDao.class)静态获取的,那需要用PowerMock来模拟静态方法:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.sql.SQLException; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; @RunWith(PowerMockRunner.class) @PrepareForTest(ManagerDao.class) // 声明要Mock静态方法的类 public class ManagerDaoTest { @Mock private DataSource dataSource; @Mock private Logger mockLogger; @Test public void getId_WhenSQLExceptionOccurs_ShouldLogError() throws SQLException { // 模拟静态LoggerFactory返回Mock的日志对象 PowerMockito.mockStatic(LoggerFactory.class); when(LoggerFactory.getLogger(ManagerDao.class)).thenReturn(mockLogger); // 模拟抛出SQLException when(dataSource.getConnection()).thenThrow(new SQLException("Test error")); // 执行方法 int result = managerDao.getId("test", 1, "test"); // 验证返回值和日志 assertEquals(0, result); verify(mockLogger).error(contains("SQLException occurred while selecting data table")); } }
内容的提问来源于stack exchange,提问作者Arpan Paliwal

