You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot中使用JUnit5和Mockito为DAO类编写测试用例

测试实现方案

前置准备

  • 确保项目引入以下依赖:JUnit Jupiter(JUnit5)、Mockito Core、Mockito Jupiter集成包
  • 你的DAO类中getSession()方法为非private可Mock方法

测试场景设计

我们覆盖3种核心业务场景:

  1. 存储过程返回成功标识,返回结果包含生成的子角色ID
  2. 存储过程返回失败标识,返回结果包含原始入参DTO
  3. 数据库操作抛出异常,正确封装为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 10:15:08