서비스 모듈에 Java Test Fixtures 플러그인 적용 가이드
이 문서는 각 서비스 모듈에 java-test-fixtures 플러그인을 적용하고 테스트 픽스처 모듈을 구성하는 방법을 설명합니다.
개요
java-test-fixtures 플러그인은 테스트에 필요한 공통 데이터와 유틸리티를 모듈 내에서 공유할 수 있게 해주는 Gradle 플러그인입니다. 이 가이드는 각 서비스 모듈에서 테스트 픽스처 모듈을 구성하고 활용하는 방법을 설명합니다.
이점
- 코드 중복 감소: 여러 테스트에서 반복적으로 사용되는 테스트 데이터를 중앙 집중화
- 유지보수성 향상: 테스트 데이터 변경 시 한 곳에서만 수정하면 됨
- 테스트 가독성 향상: 명확한 팩토리 메소드를 통해 테스트 데이터 생성 의도 전달
- 독립성 확보: 각 서비스 모듈이 자신의 테스트 픽스처를 직접 관리
- 빌드 성능 최적화: 모듈별로 필요한 테스트 픽스처만 컴파일하고 사용
적용 방법
1. 테스트 픽스처 모듈 생성
서비스 모듈 내에 test-fixtures 디렉토리를 생성하고 다음과 같이 구성합니다
services/
your-service/ # 예: rate-plan
test-fixtures/ # 테스트 픽스처 모듈
build.gradle # 테스트 픽스처 모듈 빌드 스크립트
README.md # 사용 방법 문서
src/
testFixtures/ # 테스트 픽스처 소스 코드
java/
com/you-service/test/fixtures/
data/ # 데이터 모델 픽스처
util/ # 테스트 유틸리티
annotation/ # 편의 어노테이션
{domain}/ # 도메인별 테스트 픽스처
2. 빌드 스크립트 설정
test-fixtures/build.gradle 파일을 다음과 같이 설정합니다
plugins {
id 'java'
id 'java-test-fixtures'
id 'java-library'
}
group = 'com.your-service' // 예: com.rate
version = '1.0.0'
sourceCompatibility = JavaVersion.VERSION_11 // 서비스에 맞게 조정
targetCompatibility = JavaVersion.VERSION_11 // 서비스에 맞게 조정
repositories {
mavenCentral()
maven { url "<https://<nexus>/repository/maven-public/>" }
}
dependencies {
// 기본 테스트 라이브러리
api 'org.junit.jupiter:junit-jupiter:5.9.2'
api 'org.assertj:assertj-core:3.24.2'
api 'org.mockito:mockito-core:5.2.0'
api 'org.mockito:mockito-junit-jupiter:5.2.0'
// Spring Boot 테스트 관련 의존성
api 'org.springframework.boot:spring-boot-starter-test:2.7.6' // 버전은 서비스에 맞게 조정
// TestContainers
api 'org.testcontainers:testcontainers:1.18.3'
api 'org.testcontainers:junit-jupiter:1.18.3'
api 'org.testcontainers:mysql:1.18.3' // 필요한 DB에 맞게 조정
// 도메인 특화 의존성
// 예: Kafka 관련 의존성이 필요한 경우
// api 'org.apache.avro:avro:1.8.2'
// api 'io.confluent:kafka-avro-serializer:5.0.0'
// Lombok
compileOnly 'org.projectlombok:lombok'
testFixturesCompileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testFixturesAnnotationProcessor 'org.projectlombok:lombok'
}
java {
withSourcesJar()
withJavadocJar()
}
test {
useJUnitPlatform()
}
3. 서비스 모듈의 settings.gradle 수정
서비스 모듈의 settings.gradle 파일에 테스트 픽스처 모듈을 포함합니다
rootProject.name = 'your-service' // 예: 'rate-plan'
include 'your-service-module-1' // 예: 'rate-plan-api'
include 'your-service-module-2' // 예: 'rate-plan-management'
// ... 기존 모듈들 ...
include 'test-fixtures' // 테스트 픽스처 모듈 추가
4. 서비스 하위 모듈에 테스트 픽스처 의존성 추가
각 서비스 하위 모듈의 build.gradle 파일에 테스트 픽스처 의존성을 추가합니다
dependencies {
// 기존 의존성 ...
// 테스트 픽스처 의존성 추가
testImplementation testFixtures(project(':test-fixtures'))
}
구현 가이드
1. 기본 테스트 컨테이너 유틸리티 구현
src/testFixtures/java/com/yourservice/test/fixtures/util/MySQLTestContainer.java:
package com.yourservice.test.fixtures.util;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
public class MySQLTestContainer {
private static final String MYSQL_VERSION = "mysql:8.0";
private static MySQLContainer<?> container;
public static MySQLContainer<?> getInstance() {
if (container == null) {
container = new MySQLContainer<>(DockerImageName.parse(MYSQL_VERSION))
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test")
.withReuse(true);
}
return container;
}
public static void start() {
getInstance().start();
}
public static void stop() {
if (container != null && container.isRunning()) {
container.stop();
}
}
// 편의 메소드
public static String getJdbcUrl() {
return getInstance().getJdbcUrl();
}
public static String getUsername() {
return getInstance().getUsername();
}
public static String getPassword() {
return getInstance().getPassword();
}
}
2. 통합 테스트 어노테이션 구현
src/testFixtures/java/com/yourservice/test/fixtures/annotation/IntegrationTest.java:
package com.yourservice.test.fixtures.annotation;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@Testcontainers
@ActiveProfiles("test")
public @interface IntegrationTest {
}
3. 기본 데이터 픽스처 구현
package com.yourservice.test.fixtures.data;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
@Data
@Builder
public class TestUserFixture {
private Long id;
private String username;
private String email;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public static TestUserFixture createDefault() {
return TestUserFixture.builder()
.id(1L)
.username("testUser")
.email("test@example.com")
.createdAt(LocalDateTime.now())
.updatedAt(LocalDateTime.now())
.build();
}
public static List<TestUserFixture> createTestUsers(int count) {
return Arrays.asList(
TestUserFixture.builder().id(1L).username("user1").email("user1@example.com").build(),
TestUserFixture.builder().id(2L).username("user2").email("user2@example.com").build(),
TestUserFixture.builder().id(3L).username("user3").email("user3@example.com").build()
);
}
}
4. 도메인 픽스처 구현
서비스의 도메인에 맞는 픽스처 클래스를 구현합니다. 예를 들어 요금제 서비스의 경우:
src/testFixtures/java/com/yourservice/test/fixtures/rate/RatePlanFixture.java
package com.yourservice.test.fixtures.rate;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
@Builder
public class RatePlanFixture {
private Long id;
private String name;
private String code;
private BigDecimal basePrice;
private LocalDate startDate;
private LocalDate endDate;
private boolean active;
public static RatePlanFixture createDefault() {
return RatePlanFixture.builder()
.id(1L)
.name("Standard Rate")
.code("STD-RATE")
.basePrice(new BigDecimal("100000"))
.startDate(LocalDate.now())
.endDate(LocalDate.now().plusDays(30))
.active(true)
.build();
}
public static RatePlanFixture createPromotionalRate() {
return RatePlanFixture.builder()
.id(2L)
.name("Summer Promotion")
.code("SUMMER-PROMO")
.basePrice(new BigDecimal("80000"))
.startDate(LocalDate.now())
.endDate(LocalDate.now().plusDays(14))
.active(true)
.build();
}
}
테스트 컨테이너 사용
@IntegrationTest
public class ServiceTest {
@Container
private static final MySQLContainer<?> mysqlContainer = MySQLTestContainer.getInstance();
@DynamicPropertySource
static void registerDatabaseProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", MySQLTestContainer::getJdbcUrl);
registry.add("spring.datasource.username", MySQLTestContainer::getUsername);
registry.add("spring.datasource.password", MySQLTestContainer::getPassword);
}
@Test
void testService() {
// 테스트 코드
}
}
데이터 픽스처 사용
@Test
void testWithFixtures() {
// given
TestUserFixture user = TestUserFixture.createDefault();
RatePlanFixture ratePlan = RatePlanFixture.createDefault();
// when
// 테스트 로직 실행
// then
assertThat(user.getUsername()).isEqualTo("testUser");
assertThat(ratePlan.getName()).isEqualTo("Standard Rate");
}
모범 사례
- 팩토리 메소드 패턴 사용: createDefault(), createCustom(...) 등의 팩토리 메소드로 픽스처 생성
- 충분한 주석: 픽스처의 목적과 데이터 특성을 주석으로 설명
- 불변 객체 선호: 가능한 경우 픽스처 객체를 불변으로 설계
- 의미 있는 데이터: 실제 비즈니스 시나리오를 반영하는 의미 있는 테스트 데이터 사용
- 테스트 컨테이너 재사용: withReuse(true) 옵션을 사용하여 테스트 컨테이너 재사용
- 모듈화: 관련 픽스처를 적절한 패키지로 그룹화








