Search code examples
spring-bootspring-batchjunit5

How to run @SpringBatchTest with junit5 jupiter engine


I am following below link to write end to end spring batch job test.

https://docs.spring.io/spring-batch/docs/current/reference/html/testing.html#endToEndTesting

This tells to annotate your test class with @RunWith(SpringRunner.class) which is Junit4 feature and my test case is working fine utilizing vintage engine of junit5.

Since my other non-batch related test cases are running on jupiter engine, I wanted to use same for my end to end spring batch job test. If I replace @RunWith(SpringRunner.class) with @ExtendWith(SpringExtension.class), I could see none of autowired fields got instantiated and have null value.

As I am new to both spring batch and jupiter, I am unable to understand what is going wrong.

Any pointer would be highly appreciated

Sample code of junit test case

import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
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.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringRunner;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.assertj.core.api.Assertions.assertThat;

@Slf4j
@Testcontainers
@SpringBatchTest
@SpringBootTest
@RunWith(SpringRunner.class)
//@ExtendWith(SpringExtension.class)
@ContextConfiguration(initializers = {BatchJobConfigTest.Initializer.class})
@Import({LiquibaseConfigprimary.class, LiquibaseConfigsecondary.class})
public class BatchJobConfigTest {

    @Container
    private static final PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:latest")
            .withDatabaseName("dummy-db")
            .withUsername("sa")
            .withPassword("sa");

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Autowired
    @Qualifier("primaryDatasource")
    private HikariDataSource primaryDatasource;

    @Autowired
    @Qualifier("secondaryDatasource")
    private HikariDataSource secondaryDatasource;

    @Autowired
    private SecondaryRepository secondaryRepository;

    @Autowired
    private PrimaryRepository primaryRepository;

    @Autowired
    private JobRepositoryTestUtils jobRepositoryTestUtils;

    @Autowired
    private BatchConfigurer batchConfigurer;

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @BeforeEach
    void setup() {
        secondaryRepository.deleteAll();
        primaryRepository.deleteAll();
    }

    @Test
    public void testJob() throws Exception {
        assertThat(primaryRepository.findAll()).hasSize(1);
        assertThat(secondaryRepository.findAll()).hasSize(0);
        JobExecution jobExecution = jobLauncherTestUtils.launchJob();
        assertThat(secondaryRepository.findAll()).hasSize(1);
        assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo("COMPLETED");
    }

    static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            if (!postgreSQLContainer.isRunning())
                postgreSQLContainer.start();
            TestPropertyValues.of(
                    "spring.data.postgres.url=" + postgreSQLContainer.getJdbcUrl(),
                    "spring.data.postgres.username=" + postgreSQLContainer.getUsername(),
                    "spring.data.postgres.password=" + postgreSQLContainer.getPassword(),
                    "spring.data.postgres.host=localhost",
                    "spring.data.postgres.port=" + postgreSQLContainer.getFirstMappedPort(),
                    "spring.data.postgres.database=" + postgreSQLContainer.getDatabaseName()
            ).applyTo(configurableApplicationContext.getEnvironment());

            TestPropertyValues.of(
                    "spring.datasource-secondary.jdbc-url=" + postgreSQLContainer.getJdbcUrl(),
                    "spring.datasource-secondary.username=" + postgreSQLContainer.getUsername(),
                    "spring.datasource-secondary.password=" + postgreSQLContainer.getPassword(),
                    "spring.datasource-secondary.driver-class-name=org.postgresql.Driver"
            ).applyTo(configurableApplicationContext.getEnvironment());

        }
    }
}


Solution

  • You can just get rid of @RunWith(SpringRunner.class). There is no need to add @ExtendWith(SpringExtension.class) because that's already added by @SpringBootTest - at least in current versions of Spring Boot.

    What you then have to do is change:

    import org.junit.Test;
    

    into

    import org.junit.jupiter.api.Test;
    

    because that's what tells the JUnit platform to run Jupiter tests instead of Vintage tests. Hope that will resolve your problems.