Java中SQLTimeoutException场景的JUnit编写与Sonar代码覆盖方案
问题描述
现有一段从MariaDB获取记录的Java代码,需针对SQLTimeoutException业务场景实现测试并达成Sonar代码覆盖。代码示例如下:
业务方法代码
public SFVCustomerDetails findDetailsWithCIFNumber(String cifNumber) { SFVCustomerDetails details = null; try { details = sfvDetailsDao.findDetailsWithCIFNumber(cifNumber); } catch (Exception ex) { handleSQLException(ex); } return details; }
异常处理方法代码
public void handleSQLException(Exception exception) { log.info("handleException()==>" + exception.getMessage()); List<ServiceError> errorList; if (exception instanceof SQLTimeoutException) { errorList = CommonUtils.prepareErrorList(ErrorCodeEnum.IAL_DB_TIMEOUT); } else { errorList = CommonUtils.prepareErrorList(ErrorCodeEnum.IAL_DB_GENERIC_FAILURE); } throw new IALException(errorList, new BaseRequest(), null); }
请问如何编写该场景的JUnit用例并实现Sonar代码覆盖?
解决方案
可以通过Mockito模拟DAO层抛出指定异常,结合JUnit编写测试用例,覆盖SQLTimeoutException分支及异常处理逻辑,具体步骤如下:
1. 依赖准备
确保测试依赖包含JUnit 5、Mockito核心库及mockito-junit-jupiter(用于集成JUnit 5与Mockito)。
2. 编写测试类
核心思路
- 模拟
sfvDetailsDao对象,使其调用findDetailsWithCIFNumber时抛出SQLTimeoutException - 调用目标业务方法,捕获抛出的
IALException - 验证异常中的错误码是否符合预期,同时确认分支逻辑被执行
测试代码示例
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.sql.SQLTimeoutException; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class YourServiceClassTest { // 替换为实际业务类名 @Mock private SFVDetailsDao sfvDetailsDao; // 模拟DAO对象 @InjectMocks private YourServiceClass service; // 注入目标业务类实例 @Test void findDetailsWithCIFNumber_SQLTimeoutExceptionThrown_ThrowsIALExceptionWithTimeoutCode() { // 1. 准备测试数据 String testCif = "CIF123456"; SQLTimeoutException timeoutEx = new SQLTimeoutException("DB query timed out"); // 2. 模拟DAO抛出超时异常 doThrow(timeoutEx).when(sfvDetailsDao).findDetailsWithCIFNumber(testCif); // 3. 调用方法并捕获异常 IALException thrownEx = assertThrows(IALException.class, () -> { service.findDetailsWithCIFNumber(testCif); }); // 4. 验证异常错误码 assertNotNull(thrownEx.getErrorList()); assertEquals(ErrorCodeEnum.IAL_DB_TIMEOUT, thrownEx.getErrorList().get(0).getErrorCode()); // 5. 验证DAO方法被调用 verify(sfvDetailsDao, times(1)).findDetailsWithCIFNumber(testCif); } // 补充通用DB异常分支测试,实现全分支覆盖 @Test void findDetailsWithCIFNumber_GenericExceptionThrown_ThrowsIALExceptionWithGenericCode() { String testCif = "CIF123456"; Exception genericEx = new Exception("Generic DB failure"); doThrow(genericEx).when(sfvDetailsDao).findDetailsWithCIFNumber(testCif); IALException thrownEx = assertThrows(IALException.class, () -> { service.findDetailsWithCIFNumber(testCif); }); assertEquals(ErrorCodeEnum.IAL_DB_GENERIC_FAILURE, thrownEx.getErrorList().get(0).getErrorCode()); verify(sfvDetailsDao, times(1)).findDetailsWithCIFNumber(testCif); } }
3. 达成Sonar代码覆盖
- 上述测试用例覆盖了
findDetailsWithCIFNumber的try-catch分支,以及handleSQLException中SQLTimeoutException和通用异常两个分支 - 运行测试后,Sonar会识别到所有分支已被覆盖,解决代码覆盖不足的问题
关键注意事项
- 确保
sfvDetailsDao被正确模拟,避免触发真实数据库操作 - 捕获
IALException后需验证错误码,确保异常处理逻辑符合预期 - 必须补充通用异常分支的测试,实现全分支覆盖以满足Sonar的代码覆盖要求
内容的提问来源于stack exchange,提问作者rajkumar singh
相关产品推荐
相关产品推荐

