2023. 8. 10. 14:39ㆍTechnology[Back]
초기 개발단계에서는 기존에 만들어놓은 서비스나 개발로직이 많지 않아 테스트의 효용성이 높지 않지만 개발 후반부나 운영으로 갈수록 새로 만든 서비스가 기존에 만들어놓은 서비스나 로직과 충돌되거나 예상치못하게 동작할 수 있어서 배포 전 반드시 테스트가 필요합니다. 또한 테스트 주도 개발론으로 접근하면 실패하는 테스트들을 개발하고 점차 발전시키면서 최종적으로 성공적인 로직을 개발할 수 있고 빌드 후 배포까지 하고나서 운영계에서 오류를 잡아내어 수정하는 것보다 테스트단계에서 오류를 검출해서 빌드하기도 전에 수정하는 쪽이 시간이나 비용 측면에서 훨씬 유리하기 때문에 개발에 있어 테스트는 반드시 필요하다고 할 수 있습니다. 스프링배치는 JAVA이기 때문에 JUNIT5를 이용해서 단위테스트와 통합테스트 그리고 빌드 전 테스트 수행을 자동화하는 방법에 대해 알아보겠습니다.
1. 단위테스트
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class BatchUnitTest {
@Test
@DisplayName("UnitTest_shutdownAll")
void unitTest() throws Exception {
// [ given ]
// 데이터 및 변수 세팅
// [ when ]
// 테스트 실행
// [ then ]
// 결과 확인
assertEquals(taskExecutor.getActiveCount(), 0);
}
}
단위테스트는 통합테스트와 달리 스프링 컨텍스트를 띄우거나 디비연결을 하거나 하지 않고 순수하게 각 기능을 테스트하는 용도로 사용되며 기존에 DB에서 가져오거나 스프링에서 Autowired를 사용해서 의존성 주입하던 부분을 모두 [ given ] 영역에 개발자가 테스트용도로 제공해야하며 [ when ] 에서 각 기능을 테스트 한 후 [ then ]에서 Assertions를 이용해 해당 테스트의 성공여부를 판별할 수 있습니다.
2. 통합테스트
@SpringBootTest(classes = {
메모리에 올릴 클래스.class
})
@ActiveProfiles("test")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
//@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class BatchIntegrationTest {
@Autowired
BatchScheduleServiceTest service;
@BeforeEach
public void setup() {
// 각 테스트 실행 전 환경 세팅
jobLauncherTestUtils = new JobLauncherTestUtils();
// JobLauncherTestUtils 초기화 및 설정
jobLauncher.setTaskExecutor(taskExecutor);
jobLauncherTestUtils.setJobLauncher(jobLauncher); // jobLauncher는 테스트용 JobLauncher 빈을 주입받아야 합니다.
jobLauncherTestUtils.setJobRepository(jobRepository); // jobRegistry는 테스트용 JobRegistry 빈을 주입받아야 합니다.
}
@Test
@DisplayName("IntegrationTest_Insert_DB")
public void simpleTotalTest3() throws Exception {
// [ before SetUp ]
jobLauncherTestUtils.setJob(jobInsert); // 테스트할 Job을 설정합니다.
// [ given ]
// 데이터 및 변수 세팅
// [ when ]
// 테스트 실행
// [ then ]
// 결과 확인
assertEquals(taskExecutor.getActiveCount(), 0);
}
@AfterAll
public void afterAllCallBack() {
// 모든 job이 완료되었다면
// ThreadPoolTaskExecutor 종료 요청
taskExecutor.shutdown();
// 모든 스레드가 종료될 때까지 대기
ExecutorService executorService = taskExecutor.getThreadPoolExecutor();
executorService.shutdown();
try {
if (!executorService.awaitTermination(20, TimeUnit.MINUTES)) {
// 만약 20분 이내에 스레드들이 종료되지 않으면 강제 종료
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
통합테스트는 단위테스트와 테스트 방식은 유사하지만 스프링 컨텍스트를 띄워 테스트 한다는 점에서 차이가 있습니다.
(1). @SpringBootTest 어노테이션으로 스프링 컨텍스트를 띄워서 테스트하고 메모리에 올릴 class를 지정할 수 있습니다.
(2). 테스트와 메인환경을 분리시키고자 할 때 profile을 이용해 환경을 분리할 경우 @ActiveProfiles 어노테이션으로 테스트 할 때의 profile을 지정해서 해당 profile을 제외한 다른 profile은 메모리 상에 올리지 않게 설정할 수 있습니다.
(3). @TestInstance(TestInstance.LifeCycle.PER_CLASS) 어노테이션으로 테스트할 클래스가 여러개인 경우 각 클래스의 실행환경이 다른 클래스에 영향을 주지 않도록 설정할 수 있습니다.
(4). @DirtiesContext 어노테이션의 경우 각 테스트가 끝난 후 모든 환경을 초기화해주는 유용한 기능이지만 스프링 컨텍스트를 새로 띄우기 때문에 테스트의 시간을 증가시켜 해당 어노테이션은 사용하지 않았습니다. 그 대신 @BeforeEach 어노테이션으로 선언한 부분에서 각 테스트 전 환경세팅을 초기화하는 로직을 추가하고 각 테스트 실행 시 [ Before Setup ] 부분에서 각 테스트에 필요한 환경을 설정하는 방식으로 테스트 간 환경을 독립적으로 구성하였습니다.
(5). 모든 테스트가 종료되면 @AfterAll 어노테이션이 부여된 메서드가 호출되고 해당 테스트를 정리하는 로직을 작성하시면 됩니다.
3. Jenkins에서 빌드 전 테스트 자동화
GUI 방식으로 젠킨스 설정을 진행하는 경우 Gradle Wrapper를 이용해 프로젝트를 빌드할 때 bootJar 빌드 전 기존 테스트환경을 clean 명령어로 리셋하고 test 명령어로 테스트를 수행한 후 테스트가 모두 정상적으로 수행되면 bootJar 명령어를 실행해서 빌드를 수행하도록 설정할 수 있습니다.
젠킨스에서 위처럼 빌드 시 테스트 결과도 별도로 확인이 가능합니다.
이상입니다. 지금까지 읽어주셔서 감사합니다.
'Technology[Back]' 카테고리의 다른 글
11번가 쇼핑API(1) (1) | 2023.08.14 |
---|---|
스프링 세션방식을 이용한 인증/인가 (1) | 2023.08.10 |
엘라스틱 서치 인덱스를 생성하고 스프링에서 질의하기 (1) | 2023.08.09 |
스프링 배치의 실패시 재시도 (1) | 2023.08.09 |
스프링 배치의 멀티쓰레드 (1) | 2023.08.09 |