shrikant.sharma
shrikant.sharma

Reputation: 63

spring batch test cases

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

Answers (1)

leiblix
leiblix

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

Related Questions