如何在Spring Boot中使用JUnit5和Mockito为DAO类编写测试用例
测试实现方案
前置准备
- 确保项目引入以下依赖:JUnit Jupiter(JUnit5)、Mockito Core、Mockito Jupiter集成包
- 你的DAO类中
getSession()方法为非private可Mock方法
测试场景设计
我们覆盖3种核心业务场景:
- 存储过程返回成功标识,返回结果包含生成的子角色ID
- 存储过程返回失败标识,返回结果包含原始入参DTO
- 数据库操作抛出异常,正确封装为
RTDataBaseException抛出
完整测试代码
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import javax.persistence.ParameterMode; import javax.persistence.StoredProcedureQuery; import org.hibernate.Session; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class YourDaoTest { // 用Spy修饰待测试DAO,方便Mock内部的getSession方法 @Spy @InjectMocks private YourDaoClass yourDao; @Mock private Session session; @Mock private StoredProcedureQuery storedProcedureQuery; // 测试场景1:存储过程执行成功 @Test void addParticipantSubRole_WhenSuccess_ReturnSubRoleId() throws RTDataBaseException { // 1. 构造测试数据 int testUserId = 123; int testBuId = 456; int testPlId = 789; int expectedSubRoleId = 1001; int successCode = Constants.SUCCESS_INTEGER_VALUE; String successMsg = "添加成功"; ProductLineParticipantSubRoleDTO mockDto = mock(ProductLineParticipantSubRoleDTO.class); when(mockDto.getParticipantSubRoleName()).thenReturn("测试子角色"); when(mockDto.getParticipantSubRoleDesc()).thenReturn("测试子角色描述"); // 2. Mock依赖方法返回 doReturn(session).when(yourDao).getSession(); when(session.createStoredProcedureCall("PKG_QA_PRODUCT_LINE_ADMIN.PROC_ADD_PARTICIP_SUB_ROLES")) .thenReturn(storedProcedureQuery); when(storedProcedureQuery.registerStoredProcedureParameter(anyInt(), any(), any(ParameterMode.class))) .thenReturn(storedProcedureQuery); when(storedProcedureQuery.setParameter(anyInt(), any())).thenReturn(storedProcedureQuery); when(storedProcedureQuery.getOutputParameterValue(5)).thenReturn(expectedSubRoleId); when(storedProcedureQuery.getOutputParameterValue(6)).thenReturn(successCode); when(storedProcedureQuery.getOutputParameterValue(7)).thenReturn(successMsg); // 3. 执行方法 Map<String, Object> result = yourDao.addParticipantSubRole(mockDto, testUserId, testBuId, testPlId); // 4. 断言结果 assertEquals(expectedSubRoleId, result.get(YourDaoClass.RESULT)); assertEquals(successCode, result.get(YourDaoClass.RETURN_ID)); assertEquals(successMsg, result.get(YourDaoClass.RETURN_MESSAGE)); // 验证参数传递正确 verify(storedProcedureQuery).setParameter(1, testUserId); verify(storedProcedureQuery).setParameter(2, testPlId); verify(storedProcedureQuery).setParameter(3, "测试子角色"); verify(storedProcedureQuery).setParameter(4, "测试子角色描述"); } // 测试场景2:存储过程返回失败 @Test void addParticipantSubRole_WhenFail_ReturnOriginalDto() throws RTDataBaseException { // 1. 构造测试数据 int testUserId = 123; int testBuId = 456; int testPlId = 789; int failCode = 0; // 非成功值都判定为失败 String failMsg = "角色名称重复"; ProductLineParticipantSubRoleDTO mockDto = mock(ProductLineParticipantSubRoleDTO.class); when(mockDto.getParticipantSubRoleName()).thenReturn("重复的角色名"); when(mockDto.getParticipantSubRoleDesc()).thenReturn("描述"); // 2. Mock依赖方法返回 doReturn(session).when(yourDao).getSession(); when(session.createStoredProcedureCall(anyString())).thenReturn(storedProcedureQuery); when(storedProcedureQuery.registerStoredProcedureParameter(anyInt(), any(), any(ParameterMode.class))) .thenReturn(storedProcedureQuery); when(storedProcedureQuery.setParameter(anyInt(), any())).thenReturn(storedProcedureQuery); when(storedProcedureQuery.getOutputParameterValue(5)).thenReturn(0); when(storedProcedureQuery.getOutputParameterValue(6)).thenReturn(failCode); when(storedProcedureQuery.getOutputParameterValue(7)).thenReturn(failMsg); // 3. 执行方法 Map<String, Object> result = yourDao.addParticipantSubRole(mockDto, testUserId, testBuId, testPlId); // 4. 断言结果 assertSame(mockDto, result.get(YourDaoClass.RESULT)); assertEquals(failCode, result.get(YourDaoClass.RETURN_ID)); assertEquals(failMsg, result.get(YourDaoClass.RETURN_MESSAGE)); } // 测试场景3:数据库操作抛出异常 @Test void addParticipantSubRole_WhenDbError_ThrowRTDataBaseException() { // 1. 构造测试数据 int testUserId = 123; int testBuId = 456; int testPlId = 789; ProductLineParticipantSubRoleDTO mockDto = mock(ProductLineParticipantSubRoleDTO.class); RuntimeException mockDbException = new RuntimeException("数据库连接超时"); // 2. Mock抛出异常 doReturn(session).when(yourDao).getSession(); when(session.createStoredProcedureCall(anyString())).thenThrow(mockDbException); // 3. 执行并断言异常 RTDataBaseException exception = assertThrows(RTDataBaseException.class, () -> yourDao.addParticipantSubRole(mockDto, testUserId, testBuId, testPlId)); assertEquals("Failed to add PL Sub Role in DB", exception.getMessage()); assertSame(mockDbException, exception.getCause()); } }
注意事项
- 代码中的
YourDaoClass请替换为你实际的DAO类名 - 如果
getSession()是public方法,也可以直接Mock Session后赋值给DAO,不需要用Spy - 可以根据实际业务调整常量值和异常判断逻辑
内容的提问来源于stack exchange,提问作者TharuneshG
相关产品推荐
相关产品推荐

