如何使用Spock和Groovy在Spring Batch应用程序中模拟ItemReader [英] How to mock an ItemReader in a Spring Batch application using Spock and Groovy
问题描述
我正在尝试为 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屋!