You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为含Switch Case的Excel转DB插入方法编写单元测试?

我来帮你梳理下怎么给这个loadAccount()方法写单元测试——这个方法涉及Excel读取、数据库操作,还有switch分支逻辑,咱们可以分模块拆解测试,一步步来:


第一步:先做小重构,让代码更易测试

原方法里硬编码了文件路径、直接创建FileInputStreamXSSFWorkbook,还紧耦合了connMan,这些外部依赖会让测试很难写。先做几个轻量重构:

  1. Excel数据映射逻辑从主方法里抽出来,单独写一个mapRowToAccount(Row row)方法,专门处理switch分支的单元格解析;
  2. 配置读取、Excel文件加载的逻辑抽成独立方法,比如getExcelFilePath()loadWorkbook(String filePath)
  3. connMan通过构造方法/Setter注入到类中,而不是在方法里直接使用(方便测试时替换成Mock对象)。

举个重构后的映射方法例子:

private Account mapRowToAccount(Row row) {
    Account account = new Account();
    DataFormatter formatter = new DataFormatter();
    Iterator<Cell> cellIterator = row.cellIterator();
    
    while (cellIterator.hasNext()) {
        Cell cell = cellIterator.next();
        int columnIndex = cell.getColumnIndex();
        
        switch (columnIndex) {
            case 0:
                account.setInstitution(cell.getStringCellValue());
                break;
            case 1:
                account.setDept(formatter.formatCellValue(cell));
                break;
            case 2:
                account.setHits((int) cell.getNumericCellValue());
                break;
            case 3:
                account.setVisitors((int) cell.getNumericCellValue());
                break;
        }
    }
    return account;
}

第二步:测试switch分支的映射逻辑

这部分是核心业务逻辑,不需要依赖Excel或数据库,直接MockRowCell对象就能测试每个case的处理是否正确:

@Test
void testMapRowToAccount_Case0_Institution() {
    // 模拟列索引为0的单元格,值为测试机构名
    Cell mockCell = Mockito.mock(Cell.class);
    Mockito.when(mockCell.getColumnIndex()).thenReturn(0);
    Mockito.when(mockCell.getStringCellValue()).thenReturn("Test Bank");

    // 模拟行对象,返回包含该单元格的迭代器
    Row mockRow = Mockito.mock(Row.class);
    Iterator<Cell> mockIterator = Mockito.mock(Iterator.class);
    Mockito.when(mockRow.cellIterator()).thenReturn(mockIterator);
    Mockito.when(mockIterator.hasNext()).thenReturn(true, false);
    Mockito.when(mockIterator.next()).thenReturn(mockCell);

    // 调用映射方法并断言结果
    Account result = yourService.mapRowToAccount(mockRow);
    assertEquals("Test Bank", result.getInstitution());
}

同理,你可以分别测试:

  • Case1:混合类型单元格(比如数值型的部门编码)是否被DataFormatter正确转为字符串;
  • Case2/3:数值型单元格是否正确转为int类型。

第三步:测试Excel读取逻辑

用一个真实的测试Excel文件(放在src/test/resources下),验证方法能否正确跳过表头、读取并解析所有数据行:

@Test
void testExcelReadingAndMapping() throws Exception {
    // 加载测试用Excel文件
    String testExcelPath = "src/test/resources/test_accounts.xlsx";
    Workbook workbook = new XSSFWorkbook(new FileInputStream(testExcelPath));
    Sheet sheet = workbook.getSheetAt(0);
    Iterator<Row> rowIterator = sheet.iterator();
    
    rowIterator.next(); // 跳过表头行
    Row firstDataRow = rowIterator.next();

    // 验证第一行数据解析是否正确
    Account result = yourService.mapRowToAccount(firstDataRow);
    assertEquals("China Bank", result.getInstitution());
    assertEquals("Dept-001", result.getDept());
    assertEquals(1200, result.getHits());
    assertEquals(800, result.getVisitors());
}

第四步:测试数据库批量插入逻辑

用Mockito Mock数据库相关的依赖(connManConnectionPreparedStatement),验证批量插入的参数设置、执行流程是否符合预期:

@Test
void testDatabaseBatchInsert() throws Exception {
    // Mock所有数据库依赖
    ConnectionManager mockConnMan = Mockito.mock(ConnectionManager.class);
    Connection mockConn = Mockito.mock(Connection.class);
    PreparedStatement mockStmt = Mockito.mock(PreparedStatement.class);

    Mockito.when(mockConnMan.allocateConnection()).thenReturn(mockConn);
    Mockito.when(mockConn.prepareStatement(Mockito.anyString())).thenReturn(mockStmt);
    Mockito.when(mockStmt.executeBatch()).thenReturn(new int[]{1,1,0}); // 模拟2成功1失败

    // 注入Mock的connMan到你的服务类
    YourService service = new YourService(mockConnMan);
    service.loadAccount();

    // 验证数据库操作流程
    Mockito.verify(mockStmt, Mockito.times(3)).setString(2, Mockito.anyString());
    Mockito.verify(mockStmt, Mockito.times(3)).setInt(5, Mockito.anyInt());
    Mockito.verify(mockStmt, Mockito.times(3)).addBatch();
    Mockito.verify(mockStmt).executeBatch();
    Mockito.verify(mockConn).commit();

    // 可选:验证日志输出(用SLF4J的TestLogger可以断言日志内容)
}

第五步:测试异常场景

验证当出现异常时(比如Excel文件不存在、数据库插入失败),方法是否正确执行回滚、关闭资源:

@Test
void testExceptionHandling_Rollback() throws Exception {
    ConnectionManager mockConnMan = Mockito.mock(ConnectionManager.class);
    Connection mockConn = Mockito.mock(Connection.class);
    PreparedStatement mockStmt = Mockito.mock(PreparedStatement.class);

    Mockito.when(mockConnMan.allocateConnection()).thenReturn(mockConn);
    Mockito.when(mockConn.prepareStatement(Mockito.anyString())).thenReturn(mockStmt);
    Mockito.when(mockStmt.executeBatch()).thenThrow(new SQLException("Insert failed"));

    YourService service = new YourService(mockConnMan);
    
    // 预期方法抛出异常
    assertThrows(SQLException.class, service::loadAccount);

    // 验证是否执行了回滚和资源关闭
    Mockito.verify(mockConn).rollback();
    Mockito.verify(mockConnMan).closeStatement(mockStmt);
    Mockito.verify(mockConnMan).deallocateConnection(mockConn);
}

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

火山引擎 最新活动