如何用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
相关产品推荐
相关产品推荐

