如何为含Switch Case的Excel转DB插入方法编写单元测试?
我来帮你梳理下怎么给这个loadAccount()方法写单元测试——这个方法涉及Excel读取、数据库操作,还有switch分支逻辑,咱们可以分模块拆解测试,一步步来:
第一步:先做小重构,让代码更易测试
原方法里硬编码了文件路径、直接创建FileInputStream和XSSFWorkbook,还紧耦合了connMan,这些外部依赖会让测试很难写。先做几个轻量重构:
- 把Excel数据映射逻辑从主方法里抽出来,单独写一个
mapRowToAccount(Row row)方法,专门处理switch分支的单元格解析; - 把配置读取、Excel文件加载的逻辑抽成独立方法,比如
getExcelFilePath()、loadWorkbook(String filePath); - 让
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或数据库,直接MockRow和Cell对象就能测试每个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数据库相关的依赖(connMan、Connection、PreparedStatement),验证批量插入的参数设置、执行流程是否符合预期:
@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




