BAEL-3299 - Testing a Spring Batch Job (#7982)
* BAEL-3299: First version. Broken tests * BAEL-3299: Fix tests * BAEL-3299: Include gitignore for output files * BAEL-3299: Example of writer unit test * BAEL-3299: Cleaned up and included more tests * BAEL-3299: Updated to use JobParameters * BAEL-3299: Fixed broken startup and included cleanup for tests * BAEL-3299: Fine tuned version. Fixed formatting. * BAEL-3299: Cleaned up redundant stuff * BAEL-3299: Fixed formatting * BAEL-3299: Moved source code in spring-batch module * BAEL-3299: Fixed broken tests
This commit is contained in:
1
spring-batch/.gitignore
vendored
1
spring-batch/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
output.csv
|
||||
output.json
|
||||
@@ -15,12 +15,30 @@
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- JAXB APIs & runtime no longer provided in JDK 11 -->
|
||||
<!-- see http://openjdk.java.net/jeps/320 -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>${jaxb.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>${jaxb.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- SQLite database driver -->
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>${sqlite.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-oxm</artifactId>
|
||||
@@ -32,40 +50,67 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.batch</groupId>
|
||||
<artifactId>spring-batch-core</artifactId>
|
||||
<version>${spring.batch.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.batch</groupId>
|
||||
<artifactId>spring-batch-test</artifactId>
|
||||
<version>${spring.batch.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.opencsv</groupId>
|
||||
<artifactId>opencsv</artifactId>
|
||||
<version>${opencsv.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-batch</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.5.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>${awaitility.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<spring.version>5.0.3.RELEASE</spring.version>
|
||||
<spring.batch.version>4.0.0.RELEASE</spring.batch.version>
|
||||
<spring.version>5.2.0.RELEASE</spring.version>
|
||||
<spring.batch.version>4.2.0.RELEASE</spring.batch.version>
|
||||
<spring.boot.version>2.1.9.RELEASE</spring.boot.version>
|
||||
<sqlite.version>3.15.1</sqlite.version>
|
||||
<opencsv.version>4.1</opencsv.version>
|
||||
<jaxb.version>2.3.1</jaxb.version>
|
||||
<awaitility.version>3.1.1</awaitility.version>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.baeldung.batch;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
@@ -8,6 +10,9 @@ import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
public class App {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
public static void main(final String[] args) {
|
||||
// Spring Java config
|
||||
final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
@@ -27,19 +32,16 @@ public class App {
|
||||
final JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
|
||||
final Job job = (Job) context.getBean(batchJobName);
|
||||
|
||||
System.out.println("----------------------------------------");
|
||||
System.out.println("Starting the batch job: " + batchJobName);
|
||||
LOGGER.info("Starting the batch job: {}", batchJobName);
|
||||
try {
|
||||
// To enable multiple execution of a job with the same parameters
|
||||
JobParameters jobParameters = new JobParametersBuilder()
|
||||
.addString("jobID", String.valueOf(System.currentTimeMillis()))
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("jobID", String.valueOf(System.currentTimeMillis()))
|
||||
.toJobParameters();
|
||||
final JobExecution execution = jobLauncher.run(job, jobParameters);
|
||||
System.out.println("Job Status : " + execution.getStatus());
|
||||
System.out.println("Job succeeded");
|
||||
LOGGER.info("Job Status : {}", execution.getStatus());
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Job failed");
|
||||
LOGGER.error("Job failed {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,9 +86,14 @@ public class SpringBatchConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step1(@Qualifier("itemProcessor") ItemProcessor<Transaction, Transaction> processor,
|
||||
ItemWriter<Transaction> writer) throws ParseException {
|
||||
return stepBuilderFactory.get("step1").<Transaction, Transaction>chunk(10).reader(itemReader(inputCsv)).processor(processor).writer(writer).build();
|
||||
protected Step step1(@Qualifier("itemProcessor") ItemProcessor<Transaction, Transaction> processor, ItemWriter<Transaction> writer) throws ParseException {
|
||||
return stepBuilderFactory
|
||||
.get("step1")
|
||||
.<Transaction, Transaction> chunk(10)
|
||||
.reader(itemReader(inputCsv))
|
||||
.processor(processor)
|
||||
.writer(writer)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean(name = "firstBatchJob")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.baeldung.batch.partitioner;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
@@ -7,6 +9,9 @@ import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
public class SpringbatchPartitionerApp {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SpringbatchPartitionerApp.class);
|
||||
|
||||
public static void main(final String[] args) {
|
||||
// Spring Java config
|
||||
final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
@@ -15,14 +20,13 @@ public class SpringbatchPartitionerApp {
|
||||
|
||||
final JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
|
||||
final Job job = (Job) context.getBean("partitionerJob");
|
||||
System.out.println("Starting the batch job");
|
||||
LOGGER.info("Starting the batch job");
|
||||
try {
|
||||
final JobExecution execution = jobLauncher.run(job, new JobParameters());
|
||||
System.out.println("Job Status : " + execution.getStatus());
|
||||
System.out.println("Job succeeded");
|
||||
LOGGER.info("Job Status : {}", execution.getStatus());
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Job failed");
|
||||
LOGGER.error("Job failed {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.baeldung.batchtesting;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
@SpringBootApplication
|
||||
@PropertySource("classpath:batchtesting/application.properties")
|
||||
public class SpringBatchApplication implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("transformBooksRecords")
|
||||
private Job transformBooksRecordsJob;
|
||||
|
||||
@Value("${file.input}")
|
||||
private String input;
|
||||
|
||||
@Value("${file.output}")
|
||||
private String output;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringBatchApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
JobParametersBuilder paramsBuilder = new JobParametersBuilder();
|
||||
paramsBuilder.addString("file.input", input);
|
||||
paramsBuilder.addString("file.output", output);
|
||||
jobLauncher.run(transformBooksRecordsJob, paramsBuilder.toJobParameters());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package org.baeldung.batchtesting;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.baeldung.batchtesting.model.Book;
|
||||
import org.baeldung.batchtesting.model.BookDetails;
|
||||
import org.baeldung.batchtesting.model.BookRecord;
|
||||
import org.baeldung.batchtesting.service.BookDetailsItemProcessor;
|
||||
import org.baeldung.batchtesting.service.BookItemProcessor;
|
||||
import org.baeldung.batchtesting.service.BookRecordFieldSetMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
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.core.configuration.annotation.StepScope;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.file.FlatFileItemReader;
|
||||
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
|
||||
import org.springframework.batch.item.file.mapping.FieldSetMapper;
|
||||
import org.springframework.batch.item.json.JacksonJsonObjectMarshaller;
|
||||
import org.springframework.batch.item.json.JsonFileItemWriter;
|
||||
import org.springframework.batch.item.json.builder.JsonFileItemWriterBuilder;
|
||||
import org.springframework.batch.item.support.ListItemWriter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public class SpringBatchConfiguration {
|
||||
|
||||
private static Logger LOGGER = LoggerFactory.getLogger(SpringBatchConfiguration.class);
|
||||
|
||||
private static final String[] TOKENS = { "bookname", "bookauthor", "bookformat", "isbn", "publishyear" };
|
||||
|
||||
@Autowired
|
||||
private JobBuilderFactory jobBuilderFactory;
|
||||
|
||||
@Autowired
|
||||
private StepBuilderFactory stepBuilderFactory;
|
||||
|
||||
@Bean
|
||||
@StepScope
|
||||
public FlatFileItemReader<BookRecord> csvItemReader(@Value("#{jobParameters['file.input']}") String input) {
|
||||
FlatFileItemReaderBuilder<BookRecord> builder = new FlatFileItemReaderBuilder<>();
|
||||
FieldSetMapper<BookRecord> bookRecordFieldSetMapper = new BookRecordFieldSetMapper();
|
||||
LOGGER.info("Configuring reader to input {}", input);
|
||||
// @formatter:off
|
||||
return builder
|
||||
.name("bookRecordItemReader")
|
||||
.resource(new FileSystemResource(input))
|
||||
.delimited()
|
||||
.names(TOKENS)
|
||||
.fieldSetMapper(bookRecordFieldSetMapper)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
@StepScope
|
||||
public JsonFileItemWriter<Book> jsonItemWriter(@Value("#{jobParameters['file.output']}") String output) throws IOException {
|
||||
JsonFileItemWriterBuilder<Book> builder = new JsonFileItemWriterBuilder<>();
|
||||
JacksonJsonObjectMarshaller<Book> marshaller = new JacksonJsonObjectMarshaller<>();
|
||||
LOGGER.info("Configuring writer to output {}", output);
|
||||
// @formatter:off
|
||||
return builder
|
||||
.name("bookItemWriter")
|
||||
.jsonObjectMarshaller(marshaller)
|
||||
.resource(new FileSystemResource(output))
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
@StepScope
|
||||
public ListItemWriter<BookDetails> listItemWriter() {
|
||||
return new ListItemWriter<BookDetails>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@StepScope
|
||||
public BookItemProcessor bookItemProcessor() {
|
||||
return new BookItemProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@StepScope
|
||||
public BookDetailsItemProcessor bookDetailsItemProcessor() {
|
||||
return new BookDetailsItemProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Step step1(ItemReader<BookRecord> csvItemReader, ItemWriter<Book> jsonItemWriter) throws IOException {
|
||||
// @formatter:off
|
||||
return stepBuilderFactory
|
||||
.get("step1")
|
||||
.<BookRecord, Book> chunk(3)
|
||||
.reader(csvItemReader)
|
||||
.processor(bookItemProcessor())
|
||||
.writer(jsonItemWriter)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Step step2(ItemReader<BookRecord> csvItemReader, ItemWriter<BookDetails> listItemWriter) {
|
||||
// @formatter:off
|
||||
return stepBuilderFactory
|
||||
.get("step2")
|
||||
.<BookRecord, BookDetails> chunk(3)
|
||||
.reader(csvItemReader)
|
||||
.processor(bookDetailsItemProcessor())
|
||||
.writer(listItemWriter)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean(name = "transformBooksRecords")
|
||||
public Job transformBookRecords(Step step1, Step step2) throws IOException {
|
||||
// @formatter:off
|
||||
return jobBuilderFactory
|
||||
.get("transformBooksRecords")
|
||||
.flow(step1)
|
||||
.next(step2)
|
||||
.end()
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.baeldung.batchtesting.model;
|
||||
|
||||
public class Book {
|
||||
|
||||
private String author;
|
||||
|
||||
private String name;
|
||||
|
||||
public Book() {
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Book [author=" + author + ", name=" + name + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.baeldung.batchtesting.model;
|
||||
|
||||
public class BookDetails {
|
||||
|
||||
private String bookName;
|
||||
|
||||
private String bookFormat;
|
||||
|
||||
private String publishingYear;
|
||||
|
||||
private String bookISBN;
|
||||
|
||||
public String getBookName() {
|
||||
return bookName;
|
||||
}
|
||||
|
||||
public void setBookName(String bookName) {
|
||||
this.bookName = bookName;
|
||||
}
|
||||
|
||||
public String getBookFormat() {
|
||||
return bookFormat;
|
||||
}
|
||||
|
||||
public void setBookFormat(String bookFormat) {
|
||||
this.bookFormat = bookFormat;
|
||||
}
|
||||
|
||||
public String getPublishingYear() {
|
||||
return publishingYear;
|
||||
}
|
||||
|
||||
public void setPublishingYear(String publishingYear) {
|
||||
this.publishingYear = publishingYear;
|
||||
}
|
||||
|
||||
public String getBookISBN() {
|
||||
return bookISBN;
|
||||
}
|
||||
|
||||
public void setBookISBN(String bookISBN) {
|
||||
this.bookISBN = bookISBN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BookDetails [bookName=" + bookName + ", bookFormat=" + bookFormat + ", publishingYear=" + publishingYear + ", bookISBN=" + bookISBN + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.baeldung.batchtesting.model;
|
||||
|
||||
public class BookRecord {
|
||||
|
||||
private String bookName;
|
||||
|
||||
private String bookAuthor;
|
||||
|
||||
private String bookFormat;
|
||||
|
||||
private String bookISBN;
|
||||
|
||||
private String publishingYear;
|
||||
|
||||
public void setBookName(String bookName) {
|
||||
this.bookName = bookName;
|
||||
}
|
||||
|
||||
public void setBookAuthor(String bookAuthor) {
|
||||
this.bookAuthor = bookAuthor;
|
||||
}
|
||||
|
||||
public void setBookFormat(String bookFormat) {
|
||||
this.bookFormat = bookFormat;
|
||||
}
|
||||
|
||||
public void setBookISBN(String bookISBN) {
|
||||
this.bookISBN = bookISBN;
|
||||
}
|
||||
|
||||
public void setPublishingYear(String publishingYear) {
|
||||
this.publishingYear = publishingYear;
|
||||
}
|
||||
|
||||
public String getBookName() {
|
||||
return bookName;
|
||||
}
|
||||
|
||||
public String getBookAuthor() {
|
||||
return bookAuthor;
|
||||
}
|
||||
|
||||
public String getBookFormat() {
|
||||
return bookFormat;
|
||||
}
|
||||
|
||||
public String getBookISBN() {
|
||||
return bookISBN;
|
||||
}
|
||||
|
||||
public String getPublishingYear() {
|
||||
return publishingYear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BookRecord [bookName=" + bookName + ", bookAuthor=" + bookAuthor + ", bookFormat=" + bookFormat + ", bookISBN=" + bookISBN + ", publishingYear=" + publishingYear + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.baeldung.batchtesting.service;
|
||||
|
||||
import org.baeldung.batchtesting.model.BookDetails;
|
||||
import org.baeldung.batchtesting.model.BookRecord;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
|
||||
public class BookDetailsItemProcessor implements ItemProcessor<BookRecord, BookDetails> {
|
||||
|
||||
private static Logger LOGGER = LoggerFactory.getLogger(BookDetailsItemProcessor.class);
|
||||
|
||||
@Override
|
||||
public BookDetails process(BookRecord item) throws Exception {
|
||||
BookDetails bookDetails = new BookDetails();
|
||||
bookDetails.setBookFormat(item.getBookFormat());
|
||||
bookDetails.setBookISBN(item.getBookISBN());
|
||||
bookDetails.setPublishingYear(item.getPublishingYear());
|
||||
bookDetails.setBookName(item.getBookName());
|
||||
LOGGER.info("Processing bookdetails {}", bookDetails);
|
||||
return bookDetails;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.baeldung.batchtesting.service;
|
||||
|
||||
import org.baeldung.batchtesting.model.Book;
|
||||
import org.baeldung.batchtesting.model.BookRecord;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
|
||||
public class BookItemProcessor implements ItemProcessor<BookRecord, Book> {
|
||||
|
||||
private static Logger LOGGER = LoggerFactory.getLogger(BookItemProcessor.class);
|
||||
|
||||
@Override
|
||||
public Book process(BookRecord item) throws Exception {
|
||||
Book book = new Book();
|
||||
book.setAuthor(item.getBookAuthor());
|
||||
book.setName(item.getBookName());
|
||||
LOGGER.info("Processing book {}", book);
|
||||
return book;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.baeldung.batchtesting.service;
|
||||
|
||||
import org.baeldung.batchtesting.model.BookRecord;
|
||||
import org.springframework.batch.item.file.mapping.FieldSetMapper;
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
import org.springframework.validation.BindException;
|
||||
|
||||
|
||||
public class BookRecordFieldSetMapper implements FieldSetMapper<BookRecord> {
|
||||
|
||||
@Override
|
||||
public BookRecord mapFieldSet(FieldSet fieldSet) throws BindException {
|
||||
BookRecord bookRecord = new BookRecord();
|
||||
bookRecord.setBookName(fieldSet.readString("bookname"));
|
||||
bookRecord.setBookAuthor(fieldSet.readString("bookauthor"));
|
||||
bookRecord.setBookFormat(fieldSet.readString("bookformat"));
|
||||
bookRecord.setBookISBN(fieldSet.readString("isbn"));
|
||||
bookRecord.setPublishingYear(fieldSet.readString("publishyear"));
|
||||
return bookRecord;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
spring.batch.job.enabled=false
|
||||
file.input=src/main/resources/batchtesting/input.csv
|
||||
file.output=src/main/resources/batchtesting/output.json
|
||||
8
spring-batch/src/main/resources/batchtesting/input.csv
Normal file
8
spring-batch/src/main/resources/batchtesting/input.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
Foundation,Asimov I.,hardcover,ISBN 12839,2018
|
||||
Roadside Picnic,Strugatski A.,paperback,ISBN 12839,1988
|
||||
Norwegian Wood,Murakami H.,paperback,ISBN 12839,2015
|
||||
Davinci Code,Brown D.,hardcover,ISBN 12839,2005
|
||||
Ubik,Dick K. P.,hardcover,ISBN 12839,2013
|
||||
JFK,King S.,paperback,ISBN 12839,2017
|
||||
Contact,Sagan C.,paperback,ISBN 12839,2000
|
||||
Brave New World,Huxley A.,paperback,ISBN 12839,1986
|
||||
|
@@ -8,8 +8,7 @@
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<logger name="org.baeldung.taskletsvschunks" level="debug"
|
||||
additivity="false">
|
||||
<logger name="org.baeldung.taskletsvschunks" level="debug" additivity="false">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
|
||||
@@ -17,6 +16,10 @@
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.baeldung.batchtesting" level="debug" additivity="false">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
|
||||
<root level="error">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.junit.Test;
|
||||
public class SpringContextIntegrationTest {
|
||||
|
||||
@Test
|
||||
public final void testMain() throws Exception {
|
||||
public void testMain() throws Exception {
|
||||
App.main(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.junit.Test;
|
||||
public class SpringContextTest {
|
||||
|
||||
@Test
|
||||
public final void testMain() throws Exception {
|
||||
public void testMain() throws Exception {
|
||||
App.main(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.baeldung.batchtesting;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.test.AssertFile;
|
||||
import org.springframework.batch.test.JobLauncherTestUtils;
|
||||
import org.springframework.batch.test.JobRepositoryTestUtils;
|
||||
import org.springframework.batch.test.context.SpringBatchTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBatchTest
|
||||
@EnableAutoConfiguration
|
||||
@ContextConfiguration(classes = { SpringBatchConfiguration.class })
|
||||
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
|
||||
public class SpringBatchIntegrationTest {
|
||||
|
||||
private static final String TEST_OUTPUT = "src/test/resources/output/actual-output.json";
|
||||
|
||||
private static final String EXPECTED_OUTPUT = "src/test/resources/output/expected-output.json";
|
||||
|
||||
private static final String TEST_INPUT = "src/test/resources/input/test-input.csv";
|
||||
|
||||
@Autowired
|
||||
private JobLauncherTestUtils jobLauncherTestUtils;
|
||||
|
||||
@Autowired
|
||||
private JobRepositoryTestUtils jobRepositoryTestUtils;
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
jobRepositoryTestUtils.removeJobExecutions();
|
||||
}
|
||||
|
||||
private JobParameters defaultJobParameters() {
|
||||
JobParametersBuilder paramsBuilder = new JobParametersBuilder();
|
||||
paramsBuilder.addString("file.input", TEST_INPUT);
|
||||
paramsBuilder.addString("file.output", TEST_OUTPUT);
|
||||
return paramsBuilder.toJobParameters();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenReferenceOutput_whenJobExecuted_thenSuccess() throws Exception {
|
||||
// given
|
||||
FileSystemResource expectedResult = new FileSystemResource(EXPECTED_OUTPUT);
|
||||
FileSystemResource actualResult = new FileSystemResource(TEST_OUTPUT);
|
||||
|
||||
// when
|
||||
JobExecution jobExecution = jobLauncherTestUtils.launchJob(defaultJobParameters());
|
||||
JobInstance actualJobInstance = jobExecution.getJobInstance();
|
||||
ExitStatus actualJobExitStatus = jobExecution.getExitStatus();
|
||||
|
||||
// then
|
||||
assertThat(actualJobInstance.getJobName(), is("transformBooksRecords"));
|
||||
assertThat(actualJobExitStatus.getExitCode(), is("COMPLETED"));
|
||||
AssertFile.assertFileEquals(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenReferenceOutput_whenStep1Executed_thenSuccess() throws Exception {
|
||||
|
||||
// given
|
||||
FileSystemResource expectedResult = new FileSystemResource(EXPECTED_OUTPUT);
|
||||
FileSystemResource actualResult = new FileSystemResource(TEST_OUTPUT);
|
||||
|
||||
// when
|
||||
JobExecution jobExecution = jobLauncherTestUtils.launchStep("step1", defaultJobParameters());
|
||||
Collection<StepExecution> actualStepExecutions = jobExecution.getStepExecutions();
|
||||
ExitStatus actualJobExitStatus = jobExecution.getExitStatus();
|
||||
|
||||
// then
|
||||
assertThat(actualStepExecutions.size(), is(1));
|
||||
assertThat(actualJobExitStatus.getExitCode(), is("COMPLETED"));
|
||||
AssertFile.assertFileEquals(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenStep2Executed_thenSuccess() {
|
||||
|
||||
// when
|
||||
JobExecution jobExecution = jobLauncherTestUtils.launchStep("step2", defaultJobParameters());
|
||||
Collection<StepExecution> actualStepExecutions = jobExecution.getStepExecutions();
|
||||
ExitStatus actualExitStatus = jobExecution.getExitStatus();
|
||||
|
||||
// then
|
||||
assertThat(actualStepExecutions.size(), is(1));
|
||||
assertThat(actualExitStatus.getExitCode(), is("COMPLETED"));
|
||||
actualStepExecutions.forEach(stepExecution -> {
|
||||
assertThat(stepExecution.getWriteCount(), is(8));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package org.baeldung.batchtesting;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.baeldung.batchtesting.model.Book;
|
||||
import org.baeldung.batchtesting.model.BookRecord;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.file.FlatFileItemReader;
|
||||
import org.springframework.batch.item.json.JsonFileItemWriter;
|
||||
import org.springframework.batch.test.AssertFile;
|
||||
import org.springframework.batch.test.JobRepositoryTestUtils;
|
||||
import org.springframework.batch.test.MetaDataInstanceFactory;
|
||||
import org.springframework.batch.test.StepScopeTestUtils;
|
||||
import org.springframework.batch.test.context.SpringBatchTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBatchTest
|
||||
@EnableAutoConfiguration
|
||||
@ContextConfiguration(classes = { SpringBatchConfiguration.class })
|
||||
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
|
||||
public class SpringBatchStepScopeIntegrationTest {
|
||||
|
||||
private static final String TEST_OUTPUT = "src/test/resources/output/actual-output.json";
|
||||
|
||||
private static final String EXPECTED_OUTPUT_ONE = "src/test/resources/output/expected-output-one.json";
|
||||
|
||||
private static final String TEST_INPUT_ONE = "src/test/resources/input/test-input-one.csv";
|
||||
@Autowired
|
||||
private JsonFileItemWriter<Book> jsonItemWriter;
|
||||
|
||||
@Autowired
|
||||
private FlatFileItemReader<BookRecord> itemReader;
|
||||
|
||||
@Autowired
|
||||
private JobRepositoryTestUtils jobRepositoryTestUtils;
|
||||
|
||||
private JobParameters defaultJobParameters() {
|
||||
JobParametersBuilder paramsBuilder = new JobParametersBuilder();
|
||||
paramsBuilder.addString("file.input", TEST_INPUT_ONE);
|
||||
paramsBuilder.addString("file.output", TEST_OUTPUT);
|
||||
return paramsBuilder.toJobParameters();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
jobRepositoryTestUtils.removeJobExecutions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMockedStep_whenReaderCalled_thenSuccess() throws Exception {
|
||||
|
||||
// given
|
||||
StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(defaultJobParameters());
|
||||
|
||||
// when
|
||||
StepScopeTestUtils.doInStepScope(stepExecution, () -> {
|
||||
BookRecord bookRecord;
|
||||
itemReader.open(stepExecution.getExecutionContext());
|
||||
while ((bookRecord = itemReader.read()) != null) {
|
||||
|
||||
// then
|
||||
assertThat(bookRecord.getBookName(), is("Foundation"));
|
||||
assertThat(bookRecord.getBookAuthor(), is("Asimov I."));
|
||||
assertThat(bookRecord.getBookISBN(), is("ISBN 12839"));
|
||||
assertThat(bookRecord.getBookFormat(), is("hardcover"));
|
||||
assertThat(bookRecord.getPublishingYear(), is("2018"));
|
||||
}
|
||||
itemReader.close();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMockedStep_whenWriterCalled_thenSuccess() throws Exception {
|
||||
|
||||
// given
|
||||
FileSystemResource expectedResult = new FileSystemResource(EXPECTED_OUTPUT_ONE);
|
||||
FileSystemResource actualResult = new FileSystemResource(TEST_OUTPUT);
|
||||
Book demoBook = new Book();
|
||||
demoBook.setAuthor("Grisham J.");
|
||||
demoBook.setName("The Firm");
|
||||
StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(defaultJobParameters());
|
||||
|
||||
// when
|
||||
StepScopeTestUtils.doInStepScope(stepExecution, () -> {
|
||||
|
||||
jsonItemWriter.open(stepExecution.getExecutionContext());
|
||||
jsonItemWriter.write(Arrays.asList(demoBook));
|
||||
jsonItemWriter.close();
|
||||
return null;
|
||||
});
|
||||
|
||||
// then
|
||||
AssertFile.assertFileEquals(expectedResult, actualResult);
|
||||
}
|
||||
}
|
||||
1
spring-batch/src/test/resources/input/test-input-one.csv
Normal file
1
spring-batch/src/test/resources/input/test-input-one.csv
Normal file
@@ -0,0 +1 @@
|
||||
Foundation,Asimov I.,hardcover,ISBN 12839,2018
|
||||
|
8
spring-batch/src/test/resources/input/test-input.csv
Normal file
8
spring-batch/src/test/resources/input/test-input.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
Foundation,Asimov I.,hardcover,ISBN 12839,2018
|
||||
Roadside Picnic,Strugatski A.,paperback,ISBN 12839,1988
|
||||
Norwegian Wood,Murakami H.,paperback,ISBN 12839,2015
|
||||
Davinci Code,Brown D.,hardcover,ISBN 12839,2005
|
||||
Ubik,Dick K. P.,hardcover,ISBN 12839,2013
|
||||
JFK,King S.,paperback,ISBN 12839,2017
|
||||
Contact,Sagan C.,paperback,ISBN 12839,2000
|
||||
Brave New World,Huxley A.,paperback,ISBN 12839,1986
|
||||
|
@@ -0,0 +1,3 @@
|
||||
[
|
||||
{"author":"Grisham J.","name":"The Firm"}
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
{"author":"Grisham J.","name":"The Firm"}
|
||||
]
|
||||
10
spring-batch/src/test/resources/output/expected-output.json
Normal file
10
spring-batch/src/test/resources/output/expected-output.json
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{"author":"Asimov I.","name":"Foundation"},
|
||||
{"author":"Strugatski A.","name":"Roadside Picnic"},
|
||||
{"author":"Murakami H.","name":"Norwegian Wood"},
|
||||
{"author":"Brown D.","name":"Davinci Code"},
|
||||
{"author":"Dick K. P.","name":"Ubik"},
|
||||
{"author":"King S.","name":"JFK"},
|
||||
{"author":"Sagan C.","name":"Contact"},
|
||||
{"author":"Huxley A.","name":"Brave New World"}
|
||||
]
|
||||
Reference in New Issue
Block a user