스프링 통합 테스트 패키지 구성은 테스트의 목적과 범위에 따라 결정되는 것이 좋다. 통합 테스트는 단위 테스트와는 다른 범위를 검증하므로, 코드와 테스트를 체계적으로 관리하기 위해 패키지 구조를 구분하는 것이 적합하다.
아래는 통합 테스트를 위한 패키징을 구성하는 방법과 그 이유에 대해 설명한다.
테스트 코드와 실제 코드는 분리하는 것이 좋다
src/main/java와 src/test/java를 분리하여 테스트와 실제 코드를 관리하는 것이 적합하다.src/test/java에 위치시키는 것이 좋다.단위 테스트와 통합 테스트의 패키지는 구분하는 것이 좋다
src/test/java
├── com.example.project
│ ├── unit # 단위 테스트
│ │ ├── service
│ │ ├── controller
│ │ └── repository
│ ├── integration # 통합 테스트
│ │ ├── service
│ │ ├── controller
│ │ └── repository실제 코드와 동일한 패키지 구조를 사용하는 것이 좋다
src/main/java
└── com.example.project
├── controller
├── service
└── repository
src/test/java
└── com.example.project
├── unit
│ ├── controller
│ ├── service
│ └── repository
src/test/java
└── com.example.project
├── integration
│ ├── controller
│ ├── service
│ └── repository
이 구조는 다음과 같은 장점을 제공하는 것이 적합하다:
1. 가독성을 높인다: 단위 테스트와 통합 테스트가 구분되어 각 테스트의 목적이 명확해지는 것이 좋다.
2. 접근성을 유지한다: 테스트 클래스가 실제 클래스와 동일한 패키지 구조를 따르므로, 패키지 전용 접근제어자(package-private)를 사용하는 코드에도 접근할 수 있는 것이 적합하다.
3. 유지보수성을 향상시킨다: 패키지 구조가 일관되므로, 코드 변경 시 관련 테스트를 쉽게 찾을 수 있는 것이 좋다.
통합 테스트를 기능별로 분류하는 방식은 아래와 같다.
src/test/java
└── com.example.project.integration
├── UserIntegrationTest.java # User 관련 통합 테스트
├── ItemIntegrationTest.java # Item 관련 통합 테스트
├── OrderIntegrationTest.java # Order 관련 통합 테스트
테스트 대상을 애플리케이션 레이어(Controller, Service, Repository) 기준으로 구분하는 방식은 아래와 같다.
src/test/java
└── com.example.project.integration
├── controller
│ ├── UserControllerIntegrationTest.java
│ └── ItemControllerIntegrationTest.java
├── service
│ ├── UserServiceIntegrationTest.java
│ └── ItemServiceIntegrationTest.java
└── repository
├── UserRepositoryIntegrationTest.java
└── ItemRepositoryIntegrationTest.java
통합 테스트는 아래와 같은 방식으로 구성하는 것이 적합하다.
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetUsers() throws Exception {
mockMvc.perform(get("/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(3));
}
}
@Tag로 테스트를 구분하는 것이 좋다JUnit 5의 @Tag 어노테이션을 사용하여 테스트 유형을 구분할 수도 있다.
@Tag("integration")
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest { ... }
단위 테스트와 통합 테스트를 별도로 실행하려면 빌드 도구에서 테스트 태스크를 구분하는 것이 적합하다.
Gradle에서는 테스트 태스크를 구분할 수 있는 설정이 적합하다.
tasks.test {
include '**/unit/**'
}
task integrationTest(type: Test) {
include '**/integration/**'
shouldRunAfter test
}
추천 패키징 구조는 다음과 같다:
src/test/java/com.example.project/unit/src/test/java/com.example.project/integration/실무에서는 다음 방식을 적용하는 것이 좋다:
이와 같은 방식으로 패키지 구조를 체계적으로 관리하면 테스트의 명확성과 효율성을 높이는 데 도움이 된다.