개요
레거시 프로젝트를 유지보수하고 확장해야 하는 업무를 맡게 되었다.
테스트 코드가 없는 프로젝트이다 보니 유지보수 및 확장을 위한 리팩토링에 앞서 테스트 코드를 먼저 작성해야 했다.
쿼리 개선도 필요하여 기존 동작 검증을 위해 Testcontainers 도입을 결정했는데,
상당히 과거 버전의 Java 및 Spring 프로젝트이다 보니 이에 맞춰 Testcontainers를 도입하는 과정을 기록하게 되었다.
프로젝트 스펙
레거시 프로젝트 스펙은 아래와 같다.
- JDK 7
- Spring Boot 1.5.3
- JPA + MyBatis
- Oracle 11g XE
- JUnit4
- Gradle
따라서 위 버전 기준으로 Testcontainers를 도입하는 방법을 설명한다.
Testcontainers란?
Testcontainers란 테스트에 필요한 종속성을 Docker 컨테이너로 실행하여 테스트 환경을 일관되게 유지할 수 있도록 하는 라이브러리이다.
DB 관련 테스트를 수행할 때 In-Memory DB를 사용하는 경우가 있는데,
In-Memory DB는 실제 사용하는 DB와 환경이 동일하지 않으므로 이를 대응하기 위해 주로 사용한다.
https://testcontainers.com/
이 프로젝트에서는 Oracle DB가 필요하므로 Oracle을 Testcontainers를 통해 실행할 것이다.
Docker 설치
먼저 Docker가 설치되어있지 않다면 공식 홈페이지에서 Docker Desktop을 설치하자.
https://www.docker.com/products/docker-desktop/
- Docker Engine만 설치하는 방법도 있지만,
보편적으로 Docker Desktop을 많이 사용할 것이기 때문에 해당 방법으로 설명한다.
(선택) Mac OS를 사용하고 있는 경우
Oracle은 M1 이상의 Mac OS를 지원하지 않는다.
따라서 M1 이상의 Mac OS를 사용하고 있다면 따로 추가적인 조치가 필요하다.
해당 스펙이 아니라면 아래의 프로젝트 설정 섹션부터 진행하면 된다.
Colima 설치
Testcontainers는 로컬에서 Docker를 통해 Image를 실행한다.
따라서 아래 글을 참고하여 Oracle을 구동할 수 있는 Colima를 설치하자.
https://hojun-dev.tistory.com/entry/Mac-OS에서-Docker로-Oracle-DB-띄우기
Testcontainers Desktop 설치
Testcontainers는 기기에 default로 설정된 docker 런타임을 사용하는데,
Docker Desktop이 설치되어 있다면 default 런타임이 Docker Desktop 런타임이다.
따라서 테스트 실행 시 Docker Desktop 런타임을 바꿔줘야 하는데,
Testcontainers Desktop을 설치하면 이러한 구성을 쉽게 변경할 수 있다.
참고 : https://java.testcontainers.org/supported_docker_environment/
아래 공식 홈페이지에서 Testcontainers Desktop을 설치하자.
https://testcontainers.com/desktop/
이후 Testcontainers Desktop을 실행하고, 런타임을 Colima로 바꿔주자.
앞으로 테스트를 실행할 때 Testcontainers Desktop 및 Colima를 실행하고 테스트를 진행하면 된다.
프로젝트 설정
1. build.gradle에 아래와 같이 라이브러리를 추가한다.
testImplementation 'org.testcontainers:testcontainers:1.19.8'
testImplementation 'org.testcontainers:oracle-xe:1.19.8'
- Testcontainers는 JDK 8 버전 이상만 지원하므로 테스트를 실행할 때에만 JDK 8 버전 이상으로 실행해야 한다.
2. Repository 테스트 시 공통으로 사용할 아래와 같은 클래스를 만든다.
@Import(MyBatisConfig.class) // MyBatis 의존성 추가
@DataJpaTest // JPA 테스트 의존성 추가
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 자동 테스트 DB 구성 해제
public class RepositoryTest {
private static final String IMAGE_NAME = "gvenzl/oracle-xe:11-slim-faststart"; // Oracle Docker Image
private static final String INIT_SQL = "db/init.sql"; // DB 초기화 SQL
static final OracleContainer oracleContainer;
static {
oracleContainer = new OracleContainer(IMAGE_NAME)
.usingSid()
.withInitScript(INIT_SQL);
oracleContainer.start();
System.setProperty("spring.datasource.url", oracleContainer.getJdbcUrl());
System.setProperty("spring.datasource.username", oracleContainer.getUsername());
System.setProperty("spring.datasource.password", oracleContainer.getPassword());
}
}
- Spring 상위 버전에서는
@DynamicPropertySource
를 통해 동적으로 Property를 설정할 수 있으나,
하위 버전에서는 사용할 수 없으므로System.setPropery
로 대체한다.
3. 위 클래스에서 설정한 DB 초기화 SQL 파일을 resources 폴더에 추가한다.
-- src/test/resources/db/init.sql
CREATE SEQUENCE USER_SEQ maxvalue 9999999999999999999999999 nocache;
create table USERS
(
USER_ID NUMBER not null
constraint USERS_PK
primary key,
USER_NAME VARCHAR2(50),
CREATED_AT DATE default SYSDATE
);
INSERT INTO USERS (USER_ID, USER_NAME, CREATED_AT) VALUES (USER_SEQ.NEXTVAL, 'user1', TIMESTAMP '2023-11-03 10:55:28');
INSERT INTO USERS (USER_ID, USER_NAME, CREATED_AT) VALUES (USER_SEQ.NEXTVAL, 'user2', TIMESTAMP '2023-11-03 10:55:00');
4. 테스트 클래스를 작성한다.
@RunWith(SpringRunner.class)
public class UserRepositoryTest extends RepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
public void test() {
// when
long count = userRepository.count();
// then
assertThat(count).isEqualTo(2);
}
}
5. 테스트를 실행한다.
- 최초 실행 시 Docker Image를 다운로드하여야 하여 시간이 오래 걸린다.
- 이후 실행 시에는 컨테이너 구동 소요 시간 이후 테스트가 진행된다.
마무리
레거시 프로젝트의 경우 하위 버전에 따른 제약 사항이 많아 유지보수에 어려움이 따른다.
그럴 때일수록 테스트부터 쌓아 올리며 차근차근 개선하며 좋은 프로젝트를 만들어 나가기 위해 노력해 보자.
'Java & Spring' 카테고리의 다른 글
[Spring] Spring Boot 1.x에서 JUnit5 사용하기 (1) | 2024.10.11 |
---|---|
[사내 세미나] Spring Batch 도입하기 (3) | 2024.09.04 |
Spring Boot 무료로 배포하기 (Koyeb, GitHub) (0) | 2024.03.21 |
[Spring] eventListener, transactionalEventListener 예외 및 트랜잭션 전파 총정리 (0) | 2023.12.29 |
[Spring] QueryDsl transform 및 SqmCaseSearched 오류 해결방법 with Hibernate 6.x (0) | 2023.10.05 |