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

如何用Mockito为Spring Batch Tasklet编写无数据库依赖的单元测试

解决Spring Batch Tasklet测试中expected:<FINISHED> but was:<null>的问题

问题根源

你当前测试的核心问题是:使用Mockito.mock(MyTasklet.class)创建了完全模拟的Tasklet对象。Mockito的默认行为是,mock对象的所有方法如果没有显式指定返回值,都会返回默认值(对象类型返回null,基本类型返回对应默认值)。你的测试中只stub了findAll()方法,但execute()方法没有被stub,所以调用myTasklet.execute(contrib, context)会返回null,导致断言失败。


方案一:使用Mockito Spy实现部分模拟

Spy可以保留真实对象的方法逻辑,只对特定方法进行mock。这样execute()会执行真实逻辑,findAll()会返回你预设的模拟数据。

修改后的测试代码:

@Test
public void testMyTaskletStep() throws Exception {
    // 1. 构造测试数据
    MyObject myObj = new MyObject();
    myObj.setXXX("columndata");
    List<MyObject> myobjList = new ArrayList<>();
    myobjList.add(myObj);

    // 2. 创建真实Tasklet实例(传入合法参数,避免空指针)
    JdbcTemplate mockJdbcTemplate = Mockito.mock(JdbcTemplate.class);
    MyTasklet realTasklet = new MyTasklet("test-output/", "UTF-8", mockJdbcTemplate);
    
    // 3. 创建Spy对象,保留真实Tasklet的逻辑
    MyTasklet spyTasklet = Mockito.spy(realTasklet);
    
    // 4. Stub findAll方法,返回模拟数据
    Mockito.doReturn(myobjList).when(spyTasklet).findAll();

    // 5. 准备Step上下文
    StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution();
    StepContribution contrib = new StepContribution(stepExecution);
    ChunkContext context = new ChunkContext(new StepContext(stepExecution));

    // 6. 执行测试并断言
    RepeatStatus status = spyTasklet.execute(contrib, context);
    Assert.assertEquals(RepeatStatus.FINISHED, status);
}

方案二:Mock Tasklet的依赖(JdbcTemplate)而非Tasklet本身

更贴合单元测试思路的做法是:测试真实的Tasklet逻辑,只mock它依赖的外部资源(这里是JdbcTemplate)。让Tasklet调用真实的findAll()方法,但内部的jdbcTemplate.query()会返回模拟数据。

修改后的测试代码:

@Test
public void testMyTaskletStep() throws Exception {
    // 1. 构造测试数据
    MyObject myObj = new MyObject();
    myObj.setXXX("columndata");
    List<MyObject> myobjList = new ArrayList<>();
    myobjList.add(myObj);

    // 2. Mock JdbcTemplate,stub query方法返回模拟数据
    JdbcTemplate mockJdbcTemplate = Mockito.mock(JdbcTemplate.class);
    Mockito.when(mockJdbcTemplate.query(Mockito.anyString(), Mockito.any(MyObjectRowMapper.class)))
           .thenReturn(myobjList);

    // 3. 创建真实Tasklet实例,传入mock的JdbcTemplate
    MyTasklet myTasklet = new MyTasklet("test-output/", "UTF-8", mockJdbcTemplate);

    // 4. 准备Step上下文
    StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution();
    StepContribution contrib = new StepContribution(stepExecution);
    ChunkContext context = new ChunkContext(new StepContext(stepExecution));

    // 5. 执行测试并断言
    RepeatStatus status = myTasklet.execute(contrib, context);
    Assert.assertEquals(RepeatStatus.FINISHED, status);
}

额外优化建议

  • 测试文件写入:可以使用临时目录(比如Files.createTempDirectory())作为filePath,避免测试产生的文件污染环境,测试完成后可删除临时文件。
  • 验证文件内容:如果需要验证写入的文件内容是否正确,可以读取生成的文件并断言内容符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 06:47:35