
테스트 코드를 작성하다보면 중복 코드가 많이 발생한다. 특히 멀티 모듈 환경에서는 test 폴더의 코드를 다른 모듈에서 사용할 수 없기 때문에 모듈마다 같은 코드를 반복해서 작성해야 했다. 이렇게 되면 이후에 유지보수도 어렵게 된다. 같은 내용을 모듈마다 작성하다 보니 다른 방법이 없을지 찾아보게 되었고 TestFixtures라는 것을 알게 되었다.
Gradle에서 제공하는 기능으로, 테스트 관련 코드를 다른 모듈에서 재사용할 수 있다. 예를 들어 공통으로 사용하는 Builder 클래스나 Helper 클래스를 한 곳에서 관리할 수 있다. 또한 상위 모듈의 테스트 전용 의존성까지 함께 전파되어 각 모듈에서 의존성을 중복으로 추가하지 않아도 된다. 따라서 TestFixtures를 사용하면 테스트 코드 관련 중복 코드를 최소화할 수 있고 유지보수에 용이하다.
testFixtures 폴더만 수정하면 모든 곳에 반영된다. 따라서 유지보수가 용이하다.project-root
├── core
│ ├── src/main # 일반 코드
│ ├── src/test # core 모듈 테스트
│ └── src/testFixtures # 🎯 공유할 테스트 설정
│ └── java/com/example/core
│ ├── config
│ │ └── TestContainerConfig.java
│ └── test
│ └── AcceptanceTestConfig.java
├── 하위 모듈 1 # testFixtures 의존성 사용
└── 하위 모듈 2 # testFixtures 의존성 사용
TestFixtures를 사용하면 위와 같은 멀티 모듈 환경에서 상위 모듈(core)의 src/testFixtures 디렉토리에 있는 코드를 하위 모듈에서 사용할 수 있게 된다.
plugins {
id 'java-test-fixtures' // TestFixtures 활성화
}
dependencies {
testFixturesApi 'org.springframework.boot:spring-boot-starter-test'
testFixturesImplementation 'org.testcontainers:testcontainers'
testFixturesImplementation 'org.testcontainers:junit-jupiter'
testFixturesImplementation 'org.testcontainers:mysql'
testFixturesImplementation 'org.testcontainers:jdbc'
testFixturesApi 'io.rest-assured:rest-assured'
}
java-test-fixtures : TestFixtures 기능을 활성화한다testFixturesApi : 하위 모듈에 테스트 관련 의존성을 전파한다testFixturesImplementation : 하위 모듈에 테스트 관련 의존성을 전파하지 않으며, core 모듈 내부에서만 사용된다.public abstract class <BaseAcceptanceTest {
static MySQLContainer<?> mysqlContainer;
@BeforeAll
static void startContainer() {
mysqlContainer = new MySQLContainer<>("mysql:8.0")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test")
.withReuse(true); // 컨테이너 재사용으로 속도 향상
mysqlContainer.start();
}
@AfterAll
static void stopContainer() {
if (mysqlContainer != null) {
mysqlContainer.stop();
}
}
@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysqlContainer::getJdbcUrl);
registry.add("spring.datasource.username", mysqlContainer::getUsername);
registry.add("spring.datasource.password", mysqlContainer::getPassword);
registry.add("spring.datasource.driver-class-name", () -> "com.mysql.cj.jdbc.Driver");
}
}
java-test-fixtures 플러그인 활성화 후 IntelliJ에서 New → Directory 선택 시 testFixtures 옵션이 나타난다. 반드시 src/testFixtures 폴더 하위에서 코드를 작성해야 하위 모듈에서 접근이 가능하다. 그 외 폴더에 있는 코드는 하위 모듈에서 참조할 수 없다.
dependencies {
implementation project(':core')
testImplementation(testFixtures(project(':core')))
}
하위 모듈의 build.gradle에서 testImplementation 을 통해 core 모듈의 testFixtures 코드를 참조하여 사용할 수 있다. project 안에 받아올 모듈 이름을 적어주면 된다. 만약 모듈 이름이 core-web이라면 project(':core-web')처럼 작성하면 된다. 의존성을 추가하지 않으면 하위 모듈에서는 testFixtures 코드를 불러올 수 없다.
class HealthControllerTest extends BaseAcceptanceTest {
@Test
void healthCheck() {
// given & when
ExtractableResponse<Response> response = RestAssured.given().log().all()
.when()
.get("/health/check")
.then().log().all()
.extract();
// then
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value());
}
}
의존성을 추가하고 나면 core 모듈에서 정의한 설정을 하위 모듈에서 사용할 수 있다. 여기에서는 위에서 추가한 BaseAcceptanceTest를 상속받았기 때문에 Testcontainers 설정이 자동으로 적용되어 MySQL 컨테이너가 실행된다.
TestFixtures를 사용하니 각 모듈에서 공통으로 사용하는 코드를 편리하게 관리할 수 있었다. 테스트 코드를 작성할 때 중복 코드가 발생하는 것을 보며 '이게 최선일까?'라는 생각을 계속 했는데 TestFixtures를 적용하니 코드가 확실히 정돈된 느낌을 받았다.
Testing in Java & JVM projects
Gradle TestFixtures 이용하여 테스트 코드 중복 줄이기
[Gradle] Multi Module에서 testFixtures를 이용하여 테스트 코드 중복 줄이기
테스트 의존성 관리로 높은 품질의 테스트 코드 유지하기