본문 바로가기
spring/spring batch

[Spring Batch4] 배치 Test를 위한 Configuration

by moonsiri 2020. 10. 31.
728x90
반응형

- Spring Batch Test 공식 문서 (한국어 번역)

공식 문서에 나와있는 batch job의 end to end 테스트 예제는 다음과 같습니다.

@SpringBatchTest
@RunWith(SpringRunner.class)   // 스프링 JUnit 기능을 사용하겠다는 표시
@ContextConfiguration(classes = SimpleJobConfiguration.class)  // ApplicationContext에 설정할 리소스 명시
public class SimpleJobTest {

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Test
    public void simple_job_테스트() throws Exception {
        JobExecution jobExecution = jobLauncherTestUtils.launchJob();

        Assert.assertEquals(jobExecution.getStatus(), BatchStatus.COMPLETED);
    }
}

 

Spring Batch 4.1 버전부터 @SpringBatchTest 어노테이션을 사용하면 JobLauncherTestUtils, JobRepositoryTestUtils를 포함한 스프링 배치 테스트 유틸리티를 주입할 수 있습니다.

 

/**
 * @author Mahmoud Ben Hassine
 * @since 4.1
 * @see JobLauncherTestUtils
 * @see JobRepositoryTestUtils
 * @see StepScopeTestExecutionListener
 * @see JobScopeTestExecutionListener
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@TestExecutionListeners(
    listeners = {StepScopeTestExecutionListener.class, JobScopeTestExecutionListener.class},
    mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
public @interface SpringBatchTest {
}

 

JobLauncherTestUtils 를 살펴보면 단일 Job을 자동 주입받고 있기 때문에 테스트할 Job Configuration을 @ContextConfiguration 어노테이션에 명시해 줍니다.

 

public class JobLauncherTestUtils {

	private SecureRandom secureRandom = new SecureRandom();

	/** Logger */
	protected final Log logger = LogFactory.getLog(getClass());

	private JobLauncher jobLauncher;

	private Job job;

	private JobRepository jobRepository;

	private StepRunner stepRunner;

	/**
	 * The Job instance that can be manipulated (e.g. launched) in this utility.
	 * 
	 * @param job the {@link AbstractJob} to use
	 */
	@Autowired
	public void setJob(Job job) {
		this.job = job;
	}

	/**
	 * The {@link JobRepository} to use for creating new {@link JobExecution}
	 * instances.
	 * 
	 * @param jobRepository a {@link JobRepository}
	 */
	@Autowired
	public void setJobRepository(JobRepository jobRepository) {
		this.jobRepository = jobRepository;
	}

...

 

JobRepositoryTestUtils 에서는 DataSource를 자동 주입받고 있습니다.

 

public class JobRepositoryTestUtils extends AbstractJdbcBatchMetadataDao implements InitializingBean {

	private JobRepository jobRepository;
...

	@Autowired
	public final void setDataSource(DataSource dataSource) {
		jdbcTemplate = new JdbcTemplate(dataSource);
	}

...

 


 

공식 문서의 예제 코드를 참고하여 테스트 코드를 작성하였습니다.

 

@SpringBatchTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SimpleJobConfiguration.class)
public class SimpleJobTest {

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Test
    public void simple_job_테스트() throws Exception {
        // given
        JobParameters jobParameters = new JobParametersBuilder()
            .addString("shardNumber", "2")
            .addString("version", "RB_0.0.4")
            .addString("exeDateTime", "202007241682")
            .toJobParameters();

        // when
        JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);

        // then
        Assert.assertEquals(jobExecution.getStatus(), BatchStatus.COMPLETED);
    }

}

 

오류가 발생하였습니다.

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: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

원인을 살펴보니 현재 프로젝트에서 dataSource 설정을 위해 생성한 @Configuration 클래스가 많은데 ApplicationContext가 로드되지 않아 JobRepositoryTestUtils 가 dataSource를 주입받지 못해 bean 등록을 못하여 나는 오류였습니다.

해결 방법으로는 세가지가 있습니다.

 

1. 관련 @Configuration 클래스를 모두 @ContextConfiguration에 명시

 

@SpringBatchTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SimpleJobConfiguration.class)
@EnableBatchProcessing  // (1) Spring Batch 기능 사용
@EnableConfigurationProperties  // (2) @ConfigurationProperties 어노테이션을 사용 중인 @Configuration 클래스가 존재하여 enable 처리
@TestPropertySource(properties = {"db.shard.cnt=4"})  // (3) property 값을 사용하는 @Configuration 클래스가 존재하여 해당 값 셋팅
@ContextConfiguration(classes = {
    DataSourceConfiguration.class, DbConfigValidation.class, HikariCpProperties.class, MainJdbcProperties.class, UserJdbcProperties.class
})  // (4) ApplicationContext가 로드 될 dataSource 관련 @Configuration 클래스들
public class SimpleJobTest {
    ...
}

 

다른 여러 Job Test를 위해 (1), (2), (3), (4) 을 공통으로 만들어 보았습니다.

@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@EnableBatchProcessing
@EnableConfigurationProperties
@TestPropertySource(properties = {"db.shard.cnt=4"})
@ContextConfiguration(classes = { DataSourceConfiguration.class, DbConfigValidation.class, HikariCpProperties.class, MainJdbcProperties.class, UserJdbcProperties.class})
public @interface TestBatchConfiguration {
}
@SpringBatchTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SimpleJobConfiguration.class)  // test할 job configuration 설정
@TestBatchConfiguration  // 
public class SimpleJobTest {  // error!!!
    ...
}

 

test할 job configuration 클래스를 따로 뺐더니 단일 Context 밖에 테스트를 할 수 없기 때문에 오류가 났습니다.

다중 @ContextConfigutaion 사용을 하려면 상속을 해야 합니다. (참고)

다음은 완성된 예제 코드입니다.

@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@EnableBatchProcessing
@EnableConfigurationProperties
@TestPropertySource(properties = {"db.shard.cnt=4"})
public @interface TestBatchConfiguration {
}
@TestBatchConfiguration  // 
@ContextConfiguration(classes = { DataSourceConfiguration.class, DbConfigValidation.class, HikariCpProperties.class, MainJdbcProperties.class, UserJdbcProperties.class})
public class BaseJdbcTest {
}
@SpringBatchTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SimpleJobConfiguration.class)
public class SimpleJobTest extends BaseJdbcTest {  // 상속

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Test
    public void simple_job_테스트() throws Exception {
        ...
    }
}

 

 

 

2. 관련 @Configuration 클래스가 속한 package를 @ComponentScan에 명시

 

dataSource 관련 @Configuration 클래스가 com.biz.config.db 패키지에 속해있습니다.

@ComponentScan 어노테이션을 이용하여 해당 패키지의 @Configuration 클래스들을 자동으로 IoC 컨테이너에 등록하도록 합니다.

2.1. 커스텀 어노테이션로 생성

@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@EnableBatchProcessing
@EnableConfigurationProperties
@TestPropertySource(properties = {"db.shard.cnt=4"})
@ComponentScan(basePackages = "com.biz.config.db")
public @interface TestBatchConfiguration {
}
@SpringBatchTest
@RunWith(SpringRunner.class)
@TestBatchConfiguration //
@ContextConfiguration(classes = SimpleJobConfiguration.class)
public class SimpleJobTest {

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Test
    public void simple_job_테스트() throws Exception {
        ...
    }
}

 

 

2.2. @Configuration 클래스로 생성

@Configuration
@EnableBatchProcessing
@EnableConfigurationProperties
@TestPropertySource(properties = {"db.shard.cnt=4"})
@ComponentScan(basePackages = "com.biz.config.db")
public class TestBatchConfiguration {
}
@SpringBatchTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SimpleJobConfiguration.class, TestBatchConfiguration.class)
public class SimpleJobTest {

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Test
    public void simple_job_테스트() throws Exception {
        ...
    }
}

 

 

3. @SpringBootTest 사용

@SpringBatchTest
@SpringBootTest
@RunWith(SpringRunner.class)
public class SimpleJobTest {

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Autowired
    private Job sampleJob;

    @BeforeEach
    public void setUp() {
        jobLauncherTestUtils.setJob(sampleJob);
    }

    @Test
    public void simple_job_테스트() throws Exception {
        ...
    }
}

 

728x90
반응형

댓글