Service 통합테스트를 작성한다면 Repository 테스트는 필요없을까요? #29
Replies: 3 comments
-
|
저는 Service 통합 테스트가 Repository 테스트를 완전히 대체할 수 없다고 생각합니다. Service 통합 테스트는 Repository를 경유하지만, 그래서 저는 Service 통합 테스트가 Repository를 "사용"하는 것과 1. 질문을 다시 정의하기저는 이 질문이 사실 두 가지 다른 질문을 담고 있다고 생각합니다. 질문 A: "Service 통합 테스트가 Repository 코드를 실행하니까, 커버리지 관점에서 Repository는 이미 테스트된 것 아닌가?" 질문 B: "Repository의 책임이 Service 통합 테스트에서 충분히 검증되는가?" 이 둘은 완전히 다른 질문입니다. A는 "코드가 실행되었는가"를 묻고, B는 "책임이 검증되었는가"를 묻습니다. 저는 테스트의 가치는 **커버리지(코드가 실행됐는가)가 아니라 검증(의도가 확인됐는가)**에 있다고 보기 때문에, B의 관점에서 답해야 한다고 생각합니다. 그리고 B의 답은 Repository에 어떤 책임이 있느냐에 따라 다르다입니다. Repository가 단순 위임이면 별도 테스트가 불필요할 수 있고, 고유한 복잡성이 있다면 여전히 필요합니다. 2. Service 통합 테스트가 검증하는 것Service 통합 테스트의 시선은 유스케이스가 끝까지 성립하는가입니다. @SpringBootTest
class ReservationServiceTest {
@Autowired private ReservationService reservationService;
@Autowired private ReservationRepository reservationRepository;
@Test
void 예약을_생성하면_저장되고_조회된다() {
Long id = reservationService.create(예약_요청);
assertThat(reservationRepository.findById(id)).isPresent();
}
}이 테스트가 확인하는 것은 "예약 생성이라는 유스케이스가 검증을 거쳐 저장까지 성공하는가"입니다. Repository는 이 흐름에서 저장이라는 한 가지 경로로만 동작합니다. 여기서 중요한 점은, 이 테스트는 Repository의 3. Repository에 고유한 책임이 있을 때 — 별도 테스트가 필요하다저는 Repository가 다음과 같은 책임을 가질 때는 별도 테스트가 여전히 필요하다고 생각합니다. (a) 복잡한 쿼리 이번 예약 대기 미션을 예로 들면, "특정 회원의 N번째 대기 순번을 계산하는 쿼리" 같은 게 있을 수 있습니다. public int findWaitingRank(Long memberId, Long themeId, LocalDate date, Long timeId) {
// 같은 슬롯에서 내 예약보다 먼저 생성된 대기의 개수를 세는 복잡한 쿼리
}이런 쿼리는 그 자체로 검증할 가치가 있는 로직입니다. Service 통합 테스트에서 "예약 대기를 신청하면 순번이 보인다" 정도로는, 순번 계산이 모든 경계 조건(같은 시각, 취소된 대기 제외, 동순위 처리 등)에서 정확한지 확인하기 어렵습니다. (b) 경계 조건과 케이스별 검증 조회 쿼리는 보통 여러 경계 조건을 가집니다.
이번 미션에서 논리 삭제( (c) 제약 조건과 SQL 매핑
핵심: Repository에 "검증할 가치가 있는 고유한 복잡성"이 있다면, 그것은 Repository 테스트가 책임지는 게 맞다고 생각합니다. 4. Repository가 단순 위임일 때 — 별도 테스트가 불필요할 수 있다반대로 Repository가 다음과 같은 경우라면, 저는 별도 Repository 테스트가 큰 가치를 주지 못한다고 생각합니다. public interface ReservationRepository extends JpaRepository<Reservation, Long> {
// Spring Data JPA가 제공하는 기본 메서드만 사용
}
이런 경우는 Service 통합 테스트에서 자연스럽게 거쳐 가면서 "저장-조회가 동작한다"가 확인되므로, 별도 Repository 테스트가 없어도 무방하다고 봅니다. 즉 Repository에 우리가 작성한 로직이 있는가가 갈림길입니다. 프레임워크가 만들어주는 메서드만 쓴다면 테스트 불필요, 우리가 직접 작성한 쿼리( 5. 효율성 관점 — 같은 검증이라면 더 싼 테스트로설령 Service 통합 테스트로 Repository의 어떤 동작을 검증할 수 있다 하더라도, 저는 '검증할 수 있다'와 '검증하기 적절하다'는 다르다고 생각합니다. Repository 쿼리의 경계 조건 10가지를 검증하고 싶을 때,
같은 검증이라면 더 작고 빠른 테스트로 하는 게 낫다는 것이 테스트 피라미드의 정신이라고 생각합니다. Repository의 세부 동작은 Repository 테스트에서, 유스케이스의 흐름은 Service 통합 테스트에서 — 이렇게 책임을 나누면 각 테스트가 실패했을 때 원인 파악도 명확해집니다. 만약 Service 통합 테스트가 깨졌는데 그 원인이 Repository 쿼리 버그라면, Service 테스트만 있을 경우 "Service 로직 문제인가, 쿼리 문제인가"를 다시 추적해야 합니다. Repository 테스트가 따로 있으면 어느 쪽이 깨졌는지로 바로 범위가 좁혀집니다. 6. 그렇다면 둘의 역할 분담은저는 이렇게 나누는 게 자연스럽다고 생각합니다. Repository 테스트 (슬라이스)가 책임지는 것
Service 통합 테스트가 책임지는 것
이렇게 보면 둘은 대체 관계가 아니라 검증하는 책임이 다른 보완 관계입니다. 7. 이번 미션에서 제 결론이번 예약 대기 미션 기준으로 제 입장을 정리하면, Repository에 직접 작성한 복잡한 쿼리가 있다면 → Repository 테스트는 필요하다. 예약 대기는 보통 "대기 순번 계산", "같은 슬롯의 대기 목록 조회", "예약 취소 시 첫 대기를 예약으로 승격" 같은 비단순 쿼리를 동반합니다. 이런 로직은 그 자체로 경계 조건이 많아서, Service 통합 테스트의 해피 패스만으로는 안심하기 어렵다고 생각합니다. Spring Data JPA 기본 메서드만 쓴다면 → Service 통합 테스트로 충분할 수 있다. CRUD가 프레임워크 제공 메서드로만 처리된다면 굳이 Repository 테스트를 따로 만들 실익은 적다고 봅니다. 그래서 저는 "Service 통합 테스트가 있으면 Repository 테스트는 불필요한가?"라는 질문에, Repository에 검증할 가치가 있는 우리만의 로직이 있는지를 먼저 보라고 답하고 싶습니다. 그 로직이 있다면 통합 테스트가 그것을 우연히 스쳐 지나가는 것에 의존하기보다, Repository 테스트로 의도를 명시적으로 검증하는 편이 안전하다고 생각합니다. 8. 정리
결국 두 테스트는 경쟁 관계가 아니라 다른 책임을 지는 보완 관계이고, "통합 테스트가 있으니 Repository 테스트는 빼도 된다"가 아니라 '이 Repository가 검증할 가치가 있는 로직을 가졌는가'를 매번 따져서 결정하는 것이 맞다고 생각합니다. |
Beta Was this translation helpful? Give feedback.
-
|
저는 이 질문에 대한 답으로 상황에 따라 다르다고 생각합니다. 먼저 각 계층 Service와 repository 테스트에 대한 책임을 설명해보자면 다음과 같습니다. Repository 테스트는 세부적으로 더 작은 Service에서 사용하는 부품들에 대한 테스트를 진행하는 것이고, 하지만, service에서의 테스트는 그것은 정해진 동작에서만의 기능을 검증하는 것이라고 생각합니다. 결론적으로, Repository에서 세부 부품들에 더 작성하거나 신경을 써서 만든 부분이 존재거나, 직접 작성한 쿼리가 있다면 Repository 테스트는 따로 필요하다고 생각합니다. 왜 이런 생각을 하게 될까?왜 이런 생각을 하게 되었을까?이런 질문이 왜 나오게 되었는지를 먼저 생각해보면 Service 통합테스트가 내부적으로 Repository를 거치니까, Repository 테스트는 중복이 되는거 아닌가?라는 질문을 하게 되었습니다. 하지만 여기서 두가지 테스트는 구분을 해보면 다음과 같습니다.
테스트는 코드에 대한 보호에 대해서 신뢰를 주는 만큼, 작성과 유지에 대한 비용도 많이 든다고 생각합니다. 어떤 생각의 흐름일까?## 어떤 생각의 흐름일까?Service 통합테스트는 Service를 진입점으로 시작해서 Repository와 DB까지 함께 띄워 검증하게 됩니다.
예를 들어서, Service에서는 findById만 사용하지만, Repository에서는 복잡한 조건 검색, 정렬, 페이징, 커스텀 쿼리(JPQL, QueryDSL)가 더 존재할 수 있다. 즉, Service 테스트만으로는 이러한 Repository에서의 나머지 쿼리들일 검증되지 않는다. 언제 Repository 테스트를 쓰고 언제 안쓸까?언제 Repository 테스트를 쓰고 언제 안쓸까?Repository 테스트를 따로 작성할 때:
Repository 테스트를 생략해도 무방할 때:
|
Beta Was this translation helpful? Give feedback.
-
|
미션을 수행하면서,
라는 크루들의 의견을 종종 들어, 여러분들의 생각이 궁금하여 발제해봤습니다 ㅎㅎ 제가 생각하는
서비스 통합 테스트는 테스트 대상을 Service 다만 이 자연스러운 흐름이 Repository의 책임을 확인하는 행위라고 생각하지 않습니다. findById 같이 단순한 메서드는 괜찮지만 repository의 메서드가 제대로 동작하는 지 확인하려면 repository의 테스트를 작성하여 테스트 작성 범위를 좁히는 것이 비용이 싸다고 생각합니다. 서비스 통합 테스트로 repository가 제대로 동작하는 지 확인하려면 불필요한 service 빈과 코드를 작성해야하니까요. 서비스의 통합 테스트의 목적이 repository가 제대로 동작하는 지 확인하는 것도 어색하다고 생각합니다. 저의 결론은 repository의 메서드가 검증과 제대로 동작하는 지 확인할 요소가 많고 복잡성을 지니고 있다면 repository에서 수행하는 게 비용도 싸고 테스트 코드 작성 의의에 적합하다고 생각합니다. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
이번 미션(예약 대기)에서 다들 테스트 코드를 집중적으로 다뤄보고 계신가요? ㅎㅎ
결론부터 말씀드리면
위 내용에 대한 여러분들의 의견이 궁금합니다.
의견과 그 이유를 자유롭게 적어주세요!
Beta Was this translation helpful? Give feedback.
All reactions