如何使用Spock和Groovy在Spring Batch应用程序中模拟ItemReader [英] How to mock an ItemReader in a Spring Batch application using Spock and Groovy

查看:116
本文介绍了如何使用Spock和Groovy在Spring Batch应用程序中模拟ItemReader的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为 Spring Batch 应用程序编写测试,特别是关于以下阅读器在获取记录时的交互作用从实现简单RowMapper的数据库中获取:

I'm trying to write the tests for a Spring Batch application, specifically on the interaction from the following reader when it gets records from a database implementing a simple RowMapper :

@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());
  }
}

这是批处理配置中的步骤定义:

Here is the step definition from the Batch configuration :

  @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();
  }

一切正常,除非我尝试使用以下方法进行测试:

Everything works fine, except when I try to test using the following :

@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之类的读者属性,但似乎无济于事.

I'm not able to Mock the reader properly, I tried various ways like updating reader's properties such as MaxRows, but nothing seems to work.

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

还是需要采取另一种方式来正确地在单元测试期间从数据库中操作记录?

Or does it needs to be done another way to properly manipulate the records from the database during unit tests?

更新:好的,所以我尝试了使用阅读器内部的服务进行更结构化的方式:

UPDATE: Ok so I tried a more structured way using a service inside the reader :

@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());
  }

}

在这里,我正面临无法正确模拟我的服务的问题:

Here, I'm facing the problem where I cannot mock properly my Service :

我必须在测试实用程序中缺少一些东西.

I must be missing something with the test utils.

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()
  }

但是,如果我尝试从那里嘲笑,那就可以了:

But if I try to mock from there, it works :

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
  }

那我在做什么错了?

推荐答案

在测试数据库交互时,我不会嘲笑读者.我会改用嵌入式数据库,并用测试数据填充它.这可以通过将以下bean添加到您的测试上下文中来实现:

When it comes to testing database interactions, I would not mock the reader. I would instead use an embedded database and populate it with test data. This can be achieved by adding the following bean to your test context:

@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或任何其他可嵌入的数据库.

This example uses H2, but you can use derby or HSLQ or SQLite or any other embeddable database.

这篇关于如何使用Spock和Groovy在Spring Batch应用程序中模拟ItemReader的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆