728x90
반응형

서비스 모듈에 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");
}

모범 사례

  1. 팩토리 메소드 패턴 사용: createDefault(), createCustom(...) 등의 팩토리 메소드로 픽스처 생성
  2. 충분한 주석: 픽스처의 목적과 데이터 특성을 주석으로 설명
  3. 불변 객체 선호: 가능한 경우 픽스처 객체를 불변으로 설계
  4. 의미 있는 데이터: 실제 비즈니스 시나리오를 반영하는 의미 있는 테스트 데이터 사용
  5. 테스트 컨테이너 재사용: withReuse(true) 옵션을 사용하여 테스트 컨테이너 재사용
  6. 모듈화: 관련 픽스처를 적절한 패키지로 그룹화
728x90
반응형

+ Recent posts