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

使用Spock编写Java函数单元测试时遭遇org.apache.poi.EmptyFileException

Spock测试中XSSFWorkbook抛出org.apache.poi.EmptyFileException的原因及修复

问题重现

测试用例(Spock):

def "should generate claims error file"(){
    given:
    def tempFile = new TempFile()
    def excelErrorRecord = new ExcelErrorRecord(row: 5, col: 4, message: "Test Message")
    HashMap<String, List<ExcelErrorRecord>> errorRecords = new HashMap<>();
    errorRecords.put("Medical",[excelErrorRecord])
    def fileInputStream = Mock(FileInputStream)
    def workBook = Mock(Workbook)
    def file = Mock(File)

    and:
    tempFile.getFile() >> file
    file.length() >> 100
    tempFile.getFile().getAbsolutePath() >> "Temp File.xlsx"
    new FileInputStream(tempFile.getFile().getAbsolutePath()) >> fileInputStream
    new XSSFWorkbook(fileInputStream) >> workBook

    when:
    service.generateClaimsErrorFile(errorRecords, tempFile)

    then:
    noExceptionThrown()
}

被测Java代码片段:

try (FileInputStream fis = new FileInputStream(tempFile.getFile().getAbsolutePath());
     Workbook workbook = new XSSFWorkbook(fis)) 
{}
catch(Exception e){}

原因分析

抛出EmptyFileException的核心原因是:你Mock的FileInputStream对象没有任何可读的有效数据。XSSFWorkbook初始化时会尝试读取流内容,验证是否符合XLSX文件结构,当检测到流为空或无有效文件头时,就会抛出该异常。

测试中通过Spock构造方法Mock,让new FileInputStream(...)返回一个Mock实例,但这个Mock流未配置任何可读数据,POI库无法识别为有效Excel文件流,因此报错。

修复方案

有两种可行修复思路,可根据代码架构选择:

方案1:提供有效的Excel文件流

直接构造包含空XLSX文件内容的流,替换Mock的FileInputStream,让POI库能正常初始化Workbook:

def "should generate claims error file"(){
    given:
    def tempFile = new TempFile()
    def excelErrorRecord = new ExcelErrorRecord(row: 5, col: 4, message: "Test Message")
    HashMap<String, List<ExcelErrorRecord>> errorRecords = new HashMap<>();
    errorRecords.put("Medical",[excelErrorRecord])
    def workBook = Mock(Workbook)
    def file = Mock(File)

    // 构造包含空XLSX内容的字节流
    def tempXlsxOutput = new ByteArrayOutputStream()
    new XSSFWorkbook().write(tempXlsxOutput)
    def validInputStream = new ByteArrayInputStream(tempXlsxOutput.toByteArray())

    and:
    tempFile.getFile() >> file
    file.length() >> 100
    tempFile.getFile().getAbsolutePath() >> "Temp File.xlsx"
    // 让FileInputStream返回有效流
    new FileInputStream(_) >> validInputStream
    new XSSFWorkbook(_) >> workBook

    when:
    service.generateClaimsErrorFile(errorRecords, tempFile)

    then:
    noExceptionThrown()
}

方案2:重构代码,依赖注入Workbook创建逻辑

把Workbook的创建逻辑封装成独立依赖,通过构造方法注入到service中,测试时直接Mock该依赖,完全避开流处理:

第一步:重构Java代码

// 定义Workbook工厂接口
public interface WorkbookFactory {
    Workbook create(File file) throws IOException;
}

// 实现XSSF版本的工厂
public class XSSFWorkbookFactory implements WorkbookFactory {
    @Override
    public Workbook create(File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file.getAbsolutePath())) {
            return new XSSFWorkbook(fis);
        }
    }
}

// 修改Service类,注入工厂
public class ClaimsService {
    private final WorkbookFactory workbookFactory;

    // 构造方法注入
    public ClaimsService(WorkbookFactory workbookFactory) {
        this.workbookFactory = workbookFactory;
    }

    public void generateClaimsErrorFile(Map<String, List<ExcelErrorRecord>> errorRecords, TempFile tempFile) {
        try {
            // 使用工厂创建Workbook
            Workbook workbook = workbookFactory.create(tempFile.getFile());
            // 后续业务逻辑...
        } catch (IOException e) {
            // 异常处理...
        }
    }
}

第二步:修改Spock测试用例

def "should generate claims error file"(){
    given:
    def tempFile = new TempFile()
    def excelErrorRecord = new ExcelErrorRecord(row: 5, col: 4, message: "Test Message")
    HashMap<String, List<ExcelErrorRecord>> errorRecords = new HashMap<>();
    errorRecords.put("Medical",[excelErrorRecord])
    def workBook = Mock(Workbook)
    def file = Mock(File)
    // Mock工厂对象
    def workbookFactory = Mock(WorkbookFactory)
    // 初始化service时注入Mock的工厂
    def service = new ClaimsService(workbookFactory)

    and:
    tempFile.getFile() >> file
    workbookFactory.create(file) >> workBook

    when:
    service.generateClaimsErrorFile(errorRecords, tempFile)

    then:
    noExceptionThrown()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:59:56