通过独立的基于 JUnit 5 的测试来测试 SpringBatch 作业

问题描述 投票:0回答:1

我的 Spring Boot 应用程序调用示例 Spring Batch 作业的配置如下:

MainApplication.java:

@SpringBootApplication
public class MainApplication {
    
    public static void main(String[] args) {
            
        new SpringApplicationBuilder(MainApplication.class).web(WebApplicationType.NONE).run(args);
    }

}

AggregateItemJobConfiguration.java

@Slf4j
@Configuration
@EnableBatchProcessing
public class AggregateItemJobConfiguration {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;
/*
    @Bean
       public EmbeddedDatabase dataSource() {
          return new EmbeddedDatabaseBuilder().build();
       }
    
    @Bean
       public JobRepositoryFactoryBean myJobRepository(DataSource dataSource) {
          JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
          jobRepositoryFactoryBean.setDataSource(dataSource);
           return jobRepositoryFactoryBean;
      }
*/  
    
    @Bean
    public Job job() throws Exception {
        return this.jobBuilderFactory
                .get("multilineJob")
                .incrementer(new RunIdIncrementer())
                .flow(step())
                .end()
                .build();
    }

    @Bean
    public Step step() throws Exception {
        return this.stepBuilderFactory.get("step1")
                .<Trade, Trade>chunk(1)
                .reader(reader())
                .writer(writer())
                .processor(processor())
                .build();
    }
    
    @Bean
    public ItemProcessor<Trade, Trade> processor() {
        return new ItemProcessor<Trade, Trade>() {

            @Override
            public Trade process(Trade item) throws Exception {
                log.info("In processor: "+item.getCustomer());
                item.setProcessed(true);
                return item;
            }
        };
    }
    
    
    
    @Bean
    @StepScope
    public FlatFileItemWriter<Trade> writer(
            ) {
        return new FlatFileItemWriterBuilder<Trade>()
                .name("tradeWriter")
                .resource(new FileSystemResource("target/test-outputs/20070122.testStream.multilineStep.txt"))
                .lineAggregator(new PassThroughLineAggregator<Trade>())
                .build();
    }

    @Bean
    public ItemReader<Trade> reader() {
        AggregateItemReader<Trade> aggregateItemReader = new AggregateItemReader<>();
        aggregateItemReader.setItemReader(fileItemReader());
        
        return new DequeueItemReader<>(aggregateItemReader);
    }

    
    @Bean
    @StepScope
    public FlatFileItemReader<AggregateItem<Trade>> fileItemReader(
     
            ) {

        return new FlatFileItemReaderBuilder<AggregateItem<Trade>>()
                .name("tradeFileItemReader")
                .resource(new ClassPathResource("data/20070122.teststream.multilineStep.txt"))
                .linesToSkip(0)
                .lineTokenizer(fixedFileDescriptor())
                .fieldSetMapper(fieldSetMapper())
                .build();
    }
    

    

    
    @Bean
    public AggregateItemFieldSetMapper<Trade> fieldSetMapper() {
        AggregateItemFieldSetMapper<Trade> fieldSetMapper = new AggregateItemFieldSetMapper<>();
        fieldSetMapper.setDelegate(new TradeFieldSetMapper());
        return fieldSetMapper;
    }

    @SuppressWarnings("serial")
    @Bean
    public PatternMatchingCompositeLineTokenizer fixedFileDescriptor() {

        PatternMatchingCompositeLineTokenizer lineTokenizer = new PatternMatchingCompositeLineTokenizer();

        Map<String, LineTokenizer> tokenizers = new HashMap<String, LineTokenizer>() {
            
            {
                put("BEGIN*", beginRecordTokenizer());
                put("END*", endRecordTokenizer());
                put("*", tradeRecordTokenizer());
            }
        };

        lineTokenizer.setTokenizers(tokenizers);
        return lineTokenizer;

    }

    @Bean
    public FixedLengthTokenizer beginRecordTokenizer() {
        FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
        tokenizer.setColumns(new Range[] { new Range(1, 5) });
        return tokenizer;
    }

    @Bean
    public FixedLengthTokenizer endRecordTokenizer() {
        FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
        tokenizer.setColumns(new Range[] { new Range(1, 3) });
        return tokenizer;
    }

    @Bean
    public FixedLengthTokenizer tradeRecordTokenizer() {
        FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
        tokenizer.setNames(new String[] { "ISIN", "Quantity", "Price", "Customer" });
        tokenizer.setColumns(new Range[] { 
                new Range(1, 12), 
                new Range(13, 15), 
                new Range(16, 20), 
                new Range(21, 29) });
        return tokenizer;
    }

}

当在IDE中作为Spring Boot应用程序运行时,运行成功(在我在pom.xml中添加了h2依赖之后

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

没有它(由于某种原因)它就无法工作:

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
    If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
    If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).



  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
[32m :: Spring Boot :: [39m             [2m (v2.7.14)[0;39m

[2m2023-08-22 15:55:59.664[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.provider.MainApplication  [0;39m [2m:[0;39m Starting MainApplication using Java 11.0.16.1 on VXABZS4H9100738 with PID 18040 (C:\code\STS\return-data-processor\target\classes started by x266345 in C:\code\STS\return-data-processor)
[2m2023-08-22 15:55:59.668[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.provider.MainApplication  [0;39m [2m:[0;39m No active profile set, falling back to 1 default profile: "default"
[2m2023-08-22 15:56:00.641[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mfaultConfiguringBeanFactoryPostProcessor[0;39m [2m:[0;39m No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
[2m2023-08-22 15:56:00.664[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mfaultConfiguringBeanFactoryPostProcessor[0;39m [2m:[0;39m No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
[2m2023-08-22 15:56:01.201[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mcom.zaxxer.hikari.HikariDataSource      [0;39m [2m:[0;39m HikariPool-1 - Starting...
[2m2023-08-22 15:56:01.617[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mcom.zaxxer.hikari.HikariDataSource      [0;39m [2m:[0;39m HikariPool-1 - Start completed.
[2m2023-08-22 15:56:01.949[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.b.c.r.s.JobRepositoryFactoryBean    [0;39m [2m:[0;39m No database type set, using meta data indicating: H2
[2m2023-08-22 15:56:01.976[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.b.c.l.support.SimpleJobLauncher     [0;39m [2m:[0;39m No TaskExecutor has been set, defaulting to synchronous executor.
[2m2023-08-22 15:56:02.310[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.i.endpoint.EventDrivenConsumer      [0;39m [2m:[0;39m Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
[2m2023-08-22 15:56:02.310[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.i.channel.PublishSubscribeChannel   [0;39m [2m:[0;39m Channel 'application.errorChannel' has 1 subscriber(s).
[2m2023-08-22 15:56:02.311[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.i.endpoint.EventDrivenConsumer      [0;39m [2m:[0;39m started bean '_org.springframework.integration.errorLogger'
[2m2023-08-22 15:56:02.322[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.provider.MainApplication  [0;39m [2m:[0;39m Started MainApplication in 3.163 seconds (JVM running for 8.508)
[2m2023-08-22 15:56:02.324[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.b.a.b.JobLauncherApplicationRunner  [0;39m [2m:[0;39m Running default command line with: []
[2m2023-08-22 15:56:02.401[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.b.c.l.support.SimpleJobLauncher     [0;39m [2m:[0;39m Job: [FlowJob: [name=multilineJob]] launched with the following parameters: [{run.id=1}]
[2m2023-08-22 15:56:02.447[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.batch.core.job.SimpleStepHandler    [0;39m [2m:[0;39m Executing step: [step1]
[2m2023-08-22 15:56:02.509[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Is Queue empty: true
[2m2023-08-22 15:56:02.524[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Aggregated read size: 2
[2m2023-08-22 15:56:02.524[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Queue size: 2
[2m2023-08-22 15:56:02.528[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer1
[2m2023-08-22 15:56:02.533[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Is Queue empty: false
[2m2023-08-22 15:56:02.533[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Queue size: 1
[2m2023-08-22 15:56:02.533[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer2
[2m2023-08-22 15:56:02.535[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Is Queue empty: true
[2m2023-08-22 15:56:02.536[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Aggregated read size: 3
[2m2023-08-22 15:56:02.536[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Queue size: 3
[2m2023-08-22 15:56:02.536[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer2
[2m2023-08-22 15:56:02.538[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Is Queue empty: false
[2m2023-08-22 15:56:02.538[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Queue size: 2
[2m2023-08-22 15:56:02.538[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer3
[2m2023-08-22 15:56:02.540[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Is Queue empty: false
[2m2023-08-22 15:56:02.540[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Queue size: 1
[2m2023-08-22 15:56:02.540[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36m.f.d.d.a.p.AggregateItemJobConfiguration[0;39m [2m:[0;39m In processor: customer4
[2m2023-08-22 15:56:02.543[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Is Queue empty: true
[2m2023-08-22 15:56:02.543[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mc.p.g.f.d.d.a.p.DequeueItemReader       [0;39m [2m:[0;39m Queue size: 0
[2m2023-08-22 15:56:02.545[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.batch.core.step.AbstractStep        [0;39m [2m:[0;39m Step: [step1] executed in 98ms
[2m2023-08-22 15:56:02.552[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.b.c.l.support.SimpleJobLauncher     [0;39m [2m:[0;39m Job: [FlowJob: [name=multilineJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 124ms
[2m2023-08-22 15:56:02.559[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mo.s.i.endpoint.EventDrivenConsumer      [0;39m [2m:[0;39m Removing {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
[2m2023-08-22 15:56:02.561[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mo.s.i.channel.PublishSubscribeChannel   [0;39m [2m:[0;39m Channel 'application.errorChannel' has 0 subscriber(s).
[2m2023-08-22 15:56:02.561[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mo.s.i.endpoint.EventDrivenConsumer      [0;39m [2m:[0;39m stopped bean '_org.springframework.integration.errorLogger'
[2m2023-08-22 15:56:02.583[0;39m [33m WARN[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mo.s.b.f.support.DisposableBeanAdapter   [0;39m [2m:[0;39m Custom destroy method 'close' on bean with name 'reader' threw an exception: org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.fileItemReader': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope
[2m2023-08-22 15:56:02.583[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mcom.zaxxer.hikari.HikariDataSource      [0;39m [2m:[0;39m HikariPool-1 - Shutdown initiated...
[2m2023-08-22 15:56:02.591[0;39m [32m INFO[0;39m [35m18040[0;39m [2m---[0;39m [2m[ionShutdownHook][0;39m [36mcom.zaxxer.hikari.HikariDataSource      [0;39m [2m:[0;39m HikariPool-1 - Shutdown completed.

我想重构该批处理应用程序由相应的基于JUnit 5的测试调用(将对其他批处理应用程序进行单独的测试)。像这样的东西:

AggregateItemJobTest.java

@Slf4j @SpringBatchTest @SpringJUnitConfig(AggregateItemJobConfiguration.class) class AggregateItemJobTest { @Autowired private JobLauncherTestUtils jobLauncherTestUtils; @Autowired private JobRepositoryTestUtils jobRepositoryTestUtils; private JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } @AfterEach public void cleanUp() { jobRepositoryTestUtils.removeJobExecutions(); } @Autowired public JobLauncher jobLauncher ; @Test void testJob() throws Exception { log.info("In test"); JobExecution jobExecution = jobLauncherTestUtils.launchJob(); } }
运行会产生以下结果:

org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.fileItemReader': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:383) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) .......... at at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:60) at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210) Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jobRepositoryTestUtils': Unsatisfied dependency expressed through method 'setDataSource' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:774) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:727) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:921) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:127) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:276) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:244) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90) ... 71 common frames omitted Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1801) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1357) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:766) ... 90 common frames omitted 16:05:35.641 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test class: context [DefaultTestContext@409c54f testClass = AggregateItemJobTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@3e74829 testClass = AggregateItemJobTest, locations = '{}', classes = '{class com.pru.globalpayments.feeds.downstream.dailycashreport.acquire.provider.AggregateItemJobConfiguration}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@44be0077, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@51a9ad5e, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@42b3b079, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@24c4ddae, org.springframework.batch.test.context.BatchTestContextCustomizer@4a3631f8], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]], class annotated with @DirtiesContext [false] with mode [null].
尝试根据

此处的建议单独添加数据源,会中断之前运行的MainApplication.java

执行。请在评论中查看更多说明以及相关的堆栈跟踪。

有人可以给我指出一个从 JUnit 5 测试运行 Spring Batch 作业的完整工作示例(最小但完整)。

TIA。

具有上述代码库的存储库可以在这里找到:

github.com/sleyzerzon/return-data-processor

spring spring-boot spring-batch spring-boot-test
1个回答
0
投票
我无法重现您提到的问题。通过类路径中的 H2,Spring Boot 将创建一个嵌入式数据库,Spring Batch 将自动使用该数据库。

有人可以给我指出一个从 JUnit 5 测试运行 Spring Batch 作业的完整工作示例(最小但完整)。

这是一个基于 start.spring.io 生成的项目的最小完整示例,具有 Spring Batch 和 H2 依赖项:

https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.7.15&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com .example.demo&dependencies=batch,h2

src/main/java/com/example/demo/DemoApplication.java

中的主类:

package com.example.demo; import org.springframework.batch.core.Job; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @EnableBatchProcessing @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Bean public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) { return jobs.get("job") .start(steps.get("step") .tasklet((contribution, chunkContext) -> { System.out.println("hello world"); return RepeatStatus.FINISHED; }) .build()) .build(); } }

src/test/java/com/example/demo/DemoApplicationTests.java

中的测试:

package com.example.demo; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.batch.core.ExitStatus; import org.springframework.batch.core.JobExecution; import org.springframework.batch.test.JobLauncherTestUtils; import org.springframework.batch.test.context.SpringBatchTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBatchTest @SpringBootTest class DemoApplicationTests { @Autowired private JobLauncherTestUtils jobLauncherTestUtils; @Test void contextLoads() throws Exception { //when JobExecution jobExecution = jobLauncherTestUtils.launchJob(); // then Assertions.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus()); } }
pom.xml:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.15</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

application.properties

 文件为空。

完成后,主应用程序以及测试都顺利通过。

© www.soinside.com 2019 - 2024. All rights reserved.