当前位置: 首页 > 知识库问答 >
问题:

如何使用Spock和Groovy在Spring批处理应用程序中模拟ItemReader

薛烨
2023-03-14

我正在尝试为Spring批处理应用程序编写测试,特别是在以下读取器从实现简单行映射器的数据库中获取记录时的交互:

@Component
@StepScope
public class RecordItemReader extends JdbcCursorItemReader<FooDto> {
  @Autowired
  public RecordItemReader(DataSource dataSource) {
    this.setDataSource(dataSource);
    this.setSql(AN_SQL_QUERY);
    this.setRowMapper(new RecordItemMapper());
  }
}

以下是Batch配置中的步骤定义:

  @Bean
  public Step step(RecordItemReader recordItemReader,
                   BatchSkipListener skipListener,
                   RecordItemWriter writer,
                   RecordItemProcessor processor,
                   PlatformTransactionManager transactionManager) {
    return stepBuilderFactory
      .get("step1")
      .transactionManager(transactionManager)
      .reader(recordItemReader)
      .faultTolerant()
      .skip(ParseException.class)
      .skip(UnexpectedInputException.class)
      .skipPolicy(new AlwaysSkipItemSkipPolicy())
      .listener(skipListener)
      .processor(processor)
      .writer(writer)
      .build();
  }

除了我尝试使用以下方法进行测试外,其他方法都可以正常工作:

@SpringBatchTest
@EnableAutoConfiguration
class BatchSOTest extends Specification {

  @Resource
  JobLauncherTestUtils jobLauncherTestUtils

  @Resource
  JobRepositoryTestUtils jobRepositoryTestUtils

  @Resource
  RecordItemReader recordItemReader

  def cleanup() {
    jobRepositoryTestUtils.removeJobExecutions()
  }

  def "batch init perfectly"() {
    given:
    // this does not work :
    (1.._) * recordItemReader.read() >> null

    when:
    def jobExecution = jobLauncherTestUtils.launchJob()
    def jobInstance = jobExecution.getJobInstance()
    def exitStatus = jobExecution.getExitStatus()

    then:
    jobInstance.getJobName() == "soJob"
    exitStatus.getExitCode() == ExitStatus.SUCCESS.getExitCode()
  }
}

我无法正确地模拟阅读器,我尝试了各种方法,比如更新阅读器的属性,比如MaxRows,但似乎没有任何效果。

更新读者结果的正确方法是什么?

或者在单元测试期间,是否需要通过另一种方式来正确操作html" target="_blank">数据库中的记录?

更新:好的,所以我尝试了一种更结构化的方式,在阅读器中使用服务:

@Component
public class FooDtoItemReader extends AbstractItemStreamItemReader<FooDto> {

  private List<FooDto> foos ;

  private final FooService fooService;

  @Autowired
  public FooDtoItemReader(FooService fooService) {
    this.fooService = fooService;
  }

  @Override
  public void open(ExecutionContext executionContext) {
    try {
      foos = fooService.getFoos();
...
public interface FooService {
  List<FooDto> getFoos();
}
@Service
public class FooServiceImpl implements FooService {

  @Autowired
  private FooDao fooDao;

  @Override
  public List<FooDto> getFoos() {
    return fooDao.getFoos();
  }
}
@Repository
public class FooDaoImpl extends JdbcDaoSupport implements FooDao {

  @Autowired
  DataSource dataSource;

  @PostConstruct
  private void initialize() {
    setDataSource(dataSource);
  }

  @Override
  public List<FooDto> getFoos() {
    return getJdbcTemplate().query(SELECT_SQL, new FooMapper());
  }

}

在这里,我面临着一个问题,我不能适当地嘲笑我的服务:

我一定是错过了什么与测试工具。

class BatchSOTest extends Specification {

  @Resource
  JobLauncherTestUtils jobLauncherTestUtils

  @Resource
  JobRepositoryTestUtils jobRepositoryTestUtils

  FooService       fooService       = Mock(FooService);
  FooDtoItemReader fooDtoItemReader = new FooDtoItemReader(fooService)

  def cleanup() {
    jobRepositoryTestUtils.removeJobExecutions()
  }

  def "batch init perfectly (second version)"() {
    given:
    fooDtoItemReader.open(Mock(ExecutionContext))

    and:
    // still not working from there :
    (1.._) * fooService.getFoos() >> [createFooEntity(123, "Test")]


    when:
    def jobExecution = jobLauncherTestUtils.launchJob()
    def jobInstance = jobExecution.getJobInstance()
    def exitStatus = jobExecution.getExitStatus()

    then:
    jobInstance.getJobName() == "soJob"
    exitStatus.getExitCode() == ExitStatus.SUCCESS.getExitCode()
  }

但如果我试着从那里嘲弄,它是有效的:

class FooDtoItemReaderTest extends Specification {

  FooService fooService = Mock(FooService);
  FooDtoItemReader fooDtoItemReader = new FooDtoItemReader(fooService, 0)

  def "open gets the foos and reader is initialized"() {
    given: "Foos examples"
    def foos = [
      createFooEntity(123, "A"),
      createFooEntity(456, "B")
    ]

    when: "reader is initialized"
    fooDtoItemReader.open(Mock(ExecutionContext))

    then: "service get the expected foos"
    1 * fooService.getFoos() >> foos
  }

那么我做错了什么?

共有1个答案

毛声
2023-03-14

在测试数据库交互时,我不会嘲笑读者。我会使用一个嵌入式数据库,并用测试数据填充它。这可以通过向测试上下文添加以下bean来实现:

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("/org/springframework/batch/core/schema-drop-h2.sql")
            .addScript("/org/springframework/batch/core/schema-h2.sql")
            .addScript("/schema.sql")
            .addScript("/test-data.sql")
            .build();
}

本示例使用H2,但您可以使用derby或HSLQ或SQLite或任何其他可嵌入数据库。

 类似资料:
  • 我有一个由Gradle2.4构建的Java库,它将被一些Java6应用程序、一些Java7应用程序、一些Java8应用程序和一些Groovy2.x应用程序使用。因此,为了尽可能地向后兼容,我编写的lib具有和1.6: 但是,我没有理由不能用Groovy/Spock编写单元测试。只要Groovy不是main/compile/runtime类路径的一部分,那么我就可以自由地用任何JVM语言编写测试!我

  • 我需要使用Spring batch创建批处理作业。 作业将访问oracle DB,然后获取记录,在tasklet中处理它们并提交结果。 我打算用hibernate和Spring一起处理数据。作业将通过AutoSys执行。我使用的命令行JobRunner作为切入点。 (额外信息-我使用的DynamicWebProject已转换为Gradle、STS、Spring 4.0、Hibernate 5.0,

  • 我有一个Spring Boot应用程序,它有2个模块。首先是一个web模块,它公开了一个rest API,允许用户将一些数据放入应用程序。第二个模块是一组批处理作业,它们作用于这些配置并执行后台处理。 我的下一个任务是打包应用程序并部署web应用程序以公开RESTendpoint,还创建shell脚本以使用企业调度程序启动批处理作业。 我正在使用Maven和spring-boot-maven-pl

  • 我试图为一个类编写一个单元测试,这个类使用带有库中的的Google vision API。问题是,由于某种原因,我的模拟仍然调用真正的方法,然后抛出一个NPE,这破坏了我的测试。我以前从未在模拟上见过这种行为,我想知道我是不是做错了什么,是不是Spock/Groovy中有bug,还是与Google lib有关?