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

Mockito与Spring Boot环境下JUnit测试用例问题排查与优化

业务类代码

public class EmployeeCrudOperation {
    
    public void deleteEmployee(String id) {
        StringBuilder deleteQuery = new StringBuilder("DELETE FROM EMPLOYEE WHERE ID = ?");
        PreparedStatement preparedStatement = null;
        Connection connection = null;

        try{
            //connection = DBConnection.getInstance().getConnection();
            //it contains JNDI connection code called under static block
            connection = getConnection();
            if(connection == null) {
                logger.logMessage("some text");
                return;
            }
            preparedStatement = connection.prepareStatement(deleteQuery.toString());
            preparedStatement.setString(1, id);
            int n = preparedStatement.executeUpdate();
            if(n == 0) {
                logger.logMessage("some message");
            }
            if(preparedStatement != null) {
                preparedStatement.close();
            }
        } catch(SQLException e) {
            logger.logMessage("some message");
        } catch(Exception e) {
            logger.logMessage("some message");
        } finally {
            try {
                if(preparedStatement != null) {
                    preparedStatement.close();
                }
            } catch(Exception e) {

            }
            try {
                if(connection != null) {
                    connection.close();
                }
            } catch(Exception e) {}
        }
    }
    
    public Connection getConnection() {
        return DBConnection.getInstance().getConnection(); 
        //it contains JNDI connection code called under static block
    }
}

测试用例代码

public class TestEmployeeCrudOperation {

    @Mock
    Connection connection;

    @Mock
    PreparedStatement preparedStatement;

    @Test
    public void testDeleteEmployee() {
        EmployeeCrudOperation employeeCrudOperation = new EmployeeCrudOperation();
        EmployeeCrudOperation spyEmployeeCrudOperation = Mockito.spy(employeeCrudOperation);

        try {
            
            when(spyEmployeeCrudOperation.getConnection()).thenReturn(connection);
            when(connection.preparedStatement(Mockito.any())).thenReturn(preparedStatement);
            when(preparedStatement.executeUpdate()).thenReturn(1);
            
            spyEmployeeCrudOperation.deleteEmplotee("1");
            
            verify(spyEmployeeCrudOperation, times(1)).deleteEmplotee("1");

        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

问题解答

1. 当前测试用例是否正确?

不正确,存在以下问题:

  • 方法名拼写错误:测试中调用的deleteEmplotee应为业务类中的deleteEmployee,拼写错误会导致找不到目标方法,测试逻辑直接失效。
  • 缺少Mock初始化:@Mock注解需要配合初始化操作才能生效,要么在测试类上添加@ExtendWith(MockitoExtension.class),要么在测试方法开头调用MockitoAnnotations.openMocks(this),否则connection和preparedStatement会是null。
  • Verify逻辑无效:测试核心是验证业务逻辑对依赖的调用(如数据库连接、SQL执行),而非验证deleteEmployee方法的调用次数——这属于无意义验证,因为测试代码本身就主动调用了一次。
  • 未覆盖分支场景:当前测试仅覆盖了正常执行的分支,完全忽略了连接为空、SQL执行无影响行数、异常抛出等关键场景。

2. 如何提升代码覆盖率?

要覆盖业务类所有代码分支,需针对每个场景编写测试:

  • 覆盖connection为null的分支:设置when(spyEmployeeCrudOperation.getConnection()).thenReturn(null),验证logger.logMessage是否被调用,且方法提前返回。
  • 覆盖executeUpdate返回0的分支:设置when(preparedStatement.executeUpdate()).thenReturn(0),验证logger.logMessage是否输出对应提示。
  • 覆盖SQLException分支:让connection.prepareStatement()或preparedStatement.executeUpdate()抛出SQLException,验证异常被捕获且日志输出。
  • 覆盖通用Exception分支:让getConnection()或其他步骤抛出非SQL的Exception,验证异常处理逻辑。
  • 覆盖finally块资源关闭逻辑:包括preparedStatement/connection不为空时的关闭操作,甚至模拟关闭时抛出异常的场景(确保finally块不会因异常中断)。
  • 修正测试基础错误:先修复拼写、Mock初始化等问题,确保测试能正常触发真实业务逻辑。

3. 调试时无法进入实际业务类的解决方法

  • 修正方法名拼写:之前的deleteEmplotee拼写错误会导致测试调用不存在的方法,自然无法进入业务类,需修正为正确的deleteEmployee。
  • 检查Mock初始化状态:如果@Mock未初始化,依赖对象会是null,触发业务类异常分支,跳过正常逻辑;确保Mock对象正确初始化。
  • 调整Mockito Spy行为:Mockito Spy默认调用真实方法,但如果对getConnection()等方法做了Stub,确保不会完全阻断真实逻辑执行(无需Stub的方法不要强行Stub)。
  • 检查IDE调试配置:确认IDE未开启“跳过指定类的调试”,避免业务类被排除在断点之外;同时确保断点打在业务类的正确位置。
  • 排查类加载问题:确保业务类和测试类使用同一类加载器,避免因类加载不一致导致调试无法关联业务类代码。

内容的提问来源于stack exchange,提问作者Gopal Lal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:40:02