Reputation: 63
I am very new to Spring batch and test cases, I have created a sample program which takes data from tables, updates name into uppercase, and saves back in the database.
Processor
import com.example.demo.demoWeb.model.User;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;
@Component
public class Processor implements ItemProcessor<User, User> {
@Override
public User process(User user) throws Exception {
System.out.println("Processor.process user = " + user);
user.setName(user.getName().toUpperCase());
return user;
}
}
Writer
import com.example.demo.demoWeb.model.User;
import com.example.demo.demoWeb.repository.UserRepository;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class Writer implements ItemWriter<User> {
@Autowired
UserRepository userRepository;
@Override
public void write(List<? extends User> users) throws Exception {
System.out.println("Writer.write users = " + users);
userRepository.saveAll(users);
}
}
batch config
import com.example.demo.demoWeb.model.User;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
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.launch.support.RunIdIncrementer;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JdbcPagingItemReader;
import org.springframework.batch.item.database.Order;
import org.springframework.batch.item.database.PagingQueryProvider;
import org.springframework.batch.item.database.support.H2PagingQueryProvider;
import org.springframework.batch.item.database.support.MySqlPagingQueryProvider;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableBatchProcessing
public class SpringBatchConfig {
@Bean
public Job job(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, ItemReader<User> itemReader, ItemProcessor<User, User> itemProcessor, ItemWriter<User> itemWriter) {
Step step = stepBuilderFactory.get("ETL-LOAD_FILE")
.<User, User>chunk(1)
.reader(itemReader)
.processor(itemProcessor)
.writer(itemWriter)
.build();
return jobBuilderFactory
.get("ETL-JOB")
.incrementer(new RunIdIncrementer())
.start(step).build();
}
@Bean
ItemReader<User> itemReader(DataSource dataSource) {
JdbcPagingItemReader<User> databaseReader = new JdbcPagingItemReader<>();
databaseReader.setDataSource(dataSource);
databaseReader.setPageSize(1);
PagingQueryProvider queryProvider = createQueryProvider();
databaseReader.setQueryProvider(queryProvider);
databaseReader.setRowMapper(new BeanPropertyRowMapper<>(User.class));
return databaseReader;
}
private PagingQueryProvider createQueryProvider() {
MySqlPagingQueryProvider queryProvider = new MySqlPagingQueryProvider();
queryProvider.setSelectClause("SELECT id, dept, name, salary");
queryProvider.setFromClause("FROM USER");
queryProvider.setSortKeys(sortByEmailAddressAsc());
return queryProvider;
}
private Map<String, Order> sortByEmailAddressAsc() {
Map<String, Order> sortConfiguration = new HashMap<>();
sortConfiguration.put("salary", Order.ASCENDING);
return sortConfiguration;
}
private LineMapper<User> lineMapper() {
DefaultLineMapper<User> defaultLineMapper = new DefaultLineMapper<>();
DelimitedLineTokenizer delimitedLineTokenizer = new DelimitedLineTokenizer();
delimitedLineTokenizer.setDelimiter(",");
delimitedLineTokenizer.setStrict(false);
delimitedLineTokenizer.setNames("id", "name", "dept", "salary");
BeanWrapperFieldSetMapper<User> beanWrapperFieldSetMapper = new BeanWrapperFieldSetMapper<>();
beanWrapperFieldSetMapper.setTargetType(User.class);
defaultLineMapper.setLineTokenizer(delimitedLineTokenizer);
defaultLineMapper.setFieldSetMapper(beanWrapperFieldSetMapper);
return defaultLineMapper;
}
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/Apple");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
model
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Objects;
@Entity
public class User {
@Id
private Integer id;
private String name;
private String dept;
private Integer salary;
public User(Integer id, String name, String dept, Integer salary) {
this.id = id;
this.name = name;
this.dept = dept;
this.salary = salary;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id.equals(user.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", dept='" + dept + '\'' +
", salary=" + salary +
'}';
}
}
Repository
import com.example.demo.demoWeb.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
}
Main Application
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
@EnableScheduling
@SpringBootApplication
public class DemoWebApplication {
@Autowired
JobLauncher jobLauncher;
@Autowired
Job job;
public static void main(String[] args) {
SpringApplication.run(DemoWebApplication.class, args);
}
@Scheduled(cron = "0 */1 * * * ?")
public void perform() throws Exception {
JobParameters params = new JobParametersBuilder()
.addString("JobID", String.valueOf(System.currentTimeMillis()))
.toJobParameters();
jobLauncher.run(job, params);
}
}
Gradle file
plugins {
id 'org.springframework.boot' version '2.3.0.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.example.demo'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '14'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.batch:spring-batch-test'
implementation 'mysql:mysql-connector-java'
compile 'org.apache.tomcat:tomcat-dbcp:9.0.1'
}
test {
useJUnitPlatform()
}
I have gone through 4-5 pages of google but still couldn't find any working example of spring batch with the test case. most of the example are too abstract that I couldn't relate to them.
so far I came up with this. but it's failing and I don't know what's wrong with this.
import com.example.demo.demoWeb.config.SpringBatchConfig;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
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.test.annotation.DirtiesContext;
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;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class)
@SpringBatchTest
@EnableAutoConfiguration
@ContextConfiguration(classes = {SpringBatchConfig.class})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class DemoWebApplicationTest {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Autowired
private JobRepositoryTestUtils jobRepositoryTestUtils;
@After
public void cleanUp() {
jobRepositoryTestUtils.removeJobExecutions();
}
@Test
public void launchJob() throws Exception {
//testing a job
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
//Testing a individual step
//JobExecution jobExecution = jobLauncherTestUtils.launchStep("step1");
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
}
}
I know JUnit test cases, but nothing about spring batch. please guide me on how to write spring batch-test cases.
Upvotes: 2
Views: 8454
Reputation: 696
Hard to say what exactly is wrong. However, you are using Spring Boot in your application. You can benefit from replacing annotation @ContextConfiguration by @SpringBootTest.
Spring Boot provides a @SpringBootTest annotation which can be used as an alternative to the standard spring-test @ContextConfiguration annotation when you need Spring Boot features.
Try this:
@SpringBatchTest
@SpringBootTest
@RunWith(SpringRunner.class)
public class DemoWebApplicationTest {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Autowired
private JobRepositoryTestUtils jobRepositoryTestUtils;
@After
public void cleanUp() {
jobRepositoryTestUtils.removeJobExecutions();
}
@Test
public void launchJob() throws Exception {
//testing a job
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
//Testing a individual step
//JobExecution jobExecution = jobLauncherTestUtils.launchStep("step1");
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
}
}
Upvotes: 3