반응형
환경: springboot3.1.5, spring batch5, junit5
어찌어찌 배치 프로그램은 짰는데, 테스트코드는 어떻게 짜야할지 막막했다.
심지어 이 배치는 디비에서 오늘에 해당하는 데이터를 읽어 다른 디비에 적재하는 배치인데
- "오늘"이라는 날짜 디펜덴시가 있는 데이터가 필요하고
- 이걸 타 디비에 실제로 넣어야 한다.
h2를 추가하여 로컬 배치로 돌리는 방법이 있겠지만 돌리는 날짜에 기반한 샘플 데이터를 만들어 넣는 게 좀 귀찮았고
디비 작업이야, 쿼리만 정확하면 보증되는 것이라(이미 다른 곳에서 돌고 있는 쿼리라서 실행이 보장되어 있음)
내가 검증하고 싶은 건 데이터를 정확히 꺼내오는 것이 아닌 job, step 등이 순차적으로 잘 도는지에 대해 작성하고 싶었다.
하여 db select, insert 부분을 mocking 할 수 있으면 좋겠다는 생각을 했다.
step1. get job launcher
bean으로 등록하거나
(아래 코드 테스트 안 해봄)
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestBatchConfig {
@Bean
public JobLauncherTestUtils jobLauncherTestUtils() {
return new JobLauncherTestUtils();
}
}
@SpringBootTest
@SpringBatchTest // mandatory?
@Import({TestBatchConfig.class, YourJobConfig.class}) // Replace YourJobConfig with your actual job configuration class
public class YourJobTest {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
util로 만들어 빈으로 등록
public class JobTestUtils {
@Autowired private ApplicationContext applicationContext;
@Autowired private JobRepository jobRepository;
@Autowired private JobExplorer jobExplorer;
@Autowired private JobLauncher jobLauncher;
public JobLauncherTestUtils getJobTester(String jobName) {
Job bean = applicationContext.getBean(jobName, Job.class);
JobLauncherTestUtils jobLauncherTestUtils = new JobLauncherTestUtils();
jobLauncherTestUtils.setJobLauncher(jobLauncher);
jobLauncherTestUtils.setJobRepository(jobRepository);
jobLauncherTestUtils.setJob(bean);
return jobLauncherTestUtils;
}
public JobParameters makeJobParameters(JobParameters parameters) {
return new JobParametersBuilder(jobExplorer).addJobParameters(parameters).toJobParameters();
}
...
}
@TestConfiguration
public class TestBatchConfig {
@Bean
public JobTestUtils jobTestUtils() {
return new JobTestUtils();
}
}
@ActiveProfiles("test")
@Import({TestBatchConfig.class})
@SpringBootTest
class DailyRankingJobConfigTest {
@Autowired private JobTestUtils jobTestUtils;
...
}
step2. mocking 하고자 하는 reader/writer가 빈으로 등록되어야 한다.
실제 job class에서 아래와 같이 item reader/writer가 주입되도록 하고..
@Configuration
@RequiredArgsConstructor
public class DailyRankingJobConfig {
private final DailyRankingJobParameter jobParameter;
@Qualifier("dailyRankingMatchCntReader")
private final MyBatisCursorItemReader<Ranking> dailyRankingMatchCntReader;
@Qualifier("dailyRankingGameMoneyReader")
private final MyBatisCursorItemReader<Ranking> dailyRankingGameMoneyReader;
@Qualifier("dailyRankingWriter")
private final ItemWriter<Ranking> dailyRankingWriter;
테스트 코드에도 빈을 주입하는데.. @MockBean어노테이션을 이용한다. 여기서 주의할 건 name에 꼭 빈 이름을 넣어야 한다.. 안 그럼 못 찾는 듯.. 에러가 발생한다.
@ActiveProfiles("test")
@Import({TestBatchConfig.class})
@SpringBootTest
class DailyRankingJobConfigTest {
@Autowired private JobTestUtils jobTestUtils;
@MockBean(name = "dailyRankingMatchCntReader")
private MyBatisCursorItemReader<Ranking> dailyRankingMatchCntReader;
@MockBean(name = "dailyRankingGameMoneyReader")
private MyBatisCursorItemReader<Ranking> dailyRankingGameMoneyReader;
@MockBean(name = "dailyRankingWriter")
private ItemWriter<Ranking> dailyRankingWriter;
...
@Test
@DisplayName("성공 케이스")
void job__success() throws Exception {
// given
JobParameters parameters =
new JobParametersBuilder()
.addString(
"date", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), true)
.addString("test version", UUID.randomUUID().toString(), true)
.toJobParameters();
given(dailyRankingMatchCntReader.read()).willReturn(getRanks().get(0), getRanks().get(1), null);
given(dailyRankingGameMoneyReader.read()).willReturn(getRanks().get(1), null);
doNothing().when(dailyRankingWriter).write(any());
// when
JobExecution jobExecution =
jobTestUtils
.getJobTester(DailyRankingJobConfig.JOB_NAME)
.launchJob(jobTestUtils.makeJobParameters(parameters));
// then
assertThat(jobExecution.getStatus()).isEqualTo(BatchStatus.COMPLETED);
// reader의 경우 chunk의 갯수만큼 호출
verify(dailyRankingMatchCntReader, times(3)).read();
verify(dailyRankingGameMoneyReader, times(2)).read();
// writer의 경우 chunk 당 한번 호출(여기선 갯수가 적어 스텝 당 한 번임)
final ArgumentCaptor<Chunk> captor = ArgumentCaptor.forClass(Chunk.class);
verify(dailyRankingWriter, times(2)).write(captor.capture());
List<Chunk> chunks = captor.getAllValues();
assertThat(chunks.size()).isEqualTo(2);
assertThat(chunks.get(0).size()).isEqualTo(2);
assertThat(chunks.get(1).size()).isEqualTo(1);
}
그러면 given.. willReturn/willThrow 등 기존에 사용하던 mocking 함수를 사용할 수 있게 된다!!
참고
https://jojoldu.tistory.com/236
728x90
반응형
'개발 > spring-batch' 카테고리의 다른 글
[spring batch] faultTolerant.retry 횟수와 관련된 고찰 (0) | 2024.09.27 |
---|---|
[spring-batch] job parameter와 관련된 수난기 (0) | 2024.08.23 |
[spring-batch] springboot3 mybatis 설정 그리고 mapper (0) | 2024.08.16 |
[spring-batch] 소개 (0) | 2023.12.04 |
[spring-batch] simpleJob (0) | 2022.05.26 |