비즈니스 요구사항을 상위 레벨에서 검증하기 위함.
컨트롤러 테스트는 애플리케이션의 외부 레이어(HTTP 요청/응답 처리)를 검증하는 역할을 하며, 사용자 요청(HTTP API 호출)이 어떻게 처리되고 어떤 결과를 반환해야 하는지를 상위 레벨에서 설계하고 확인할 수 있음.
-> 이를 통해 비즈니스 요구사항을 상위 레벨에서 정의한 후, 하위 레이어(서비스, 리포지토리)를 구현하는 방식으로 개발할 수 있음.
HTTP 인터페이스 설계를 명확히 하기 위함.
컨트롤러 테스트를 먼저 작성하면 HTTP 인터페이스(API 설계)가 명확해짐.
-> 어떤 URL로 어떤 HTTP 메서드(POST, GET, PUT, DELETE)를 사용하며, 요청 및 응답 데이터 형식이 어떻게 생겼는지를 컨트롤러 테스트 작성 단계에서 자연스럽게 정의할 수 있음. (외부와의 상호작용 방식을 먼저 확정)
현재 프로젝트에서 상위 레벨부터 하위 레벨로 내려가는 설계 방식을 따르고 있으므로
컨트롤러 테스트를 먼저 작성하고, 이에 맞는 서비스 테스트 및 로직을 구현하는 방식은 상위 레벨부터 하위 레벨로 내려가는 설계 방식(Top-Down Approach)임.
-> 이 방식은 요구사항 흐름을 따라가기 쉽고, 로직 누락이나 중복을 방지할 수 있다.
초기 단계에서 서비스 레이어가 복잡하지 않을 가능성이 있기 때문.
초기에 서비스 레이어가 단순하거나 특정 규칙만 처리해야 한다면, 컨트롤러 테스트를 작성하면서 자연스럽게 서비스 레이어를 구현할 수 있다. -> 빠른 개발을 진행할 수 있게 됨.
(결론: 나는 TDD의 초보자이므로 사용자 요청/응답 관점에서 시작하는 것이 더 직관적임.)
즉, 서비스 테스트를 먼저 작성하면:
1. 진짜 중요한 규칙을 먼저 만들고
2. 나중에 API가 바뀌어도 핵심 기능은 안전하며
3. 사용자 입장에서 중요한 기능을 먼저 검증할 수 있다는 장점이 있다.
(이러한 접근 방식은 Martin Fowler의 글이나 Clean Architecture(Robert C. Martin)와 같은 현대적인 아키텍처 패턴에서도 강조되고 있음)
현재 진행중인 커넥티 SNS 어플리케이션에서 게시물 작성 기능을 예시로 들어 TDD 접근 방식의 차이를 알아본다.
1. 컨트롤러 테스트 먼저 작성하는 경우
@Test
void 게시물_작성_성공() {
// given
PostCreateRequest request = new PostCreateRequest("제목입니다", "내용입니다", List.of("image1.jpg"));
// when
ResponseEntity<PostResponse> response = postController.createPost(request);
// then
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(response.getBody().getId()).isNotNull();
}
이 방식은 HTTP 응답이나 요청 형식에 먼저 집중하게 된다. 실제 중요한 비즈니스 로직은 아직 테스트되지 않은 상태다.
2. 서비스 테스트 먼저 작성하는 경우
@Test
void 게시물_작성시_이미지는_최대_5장까지_가능하다() {
// given
List<String> tooManyImages = List.of("1.jpg", "2.jpg", "3.jpg", "4.jpg", "5.jpg", "6.jpg");
// when & then
assertThatThrownBy(() ->
postService.createPost("제목", "내용", tooManyImages))
.isInstanceOf(TooManyImagesException.class);
}
@Test
void 게시물_내용은_최대_500자까지_가능하다() {
// given
String tooLongContent = "A".repeat(501);
// when & then
assertThatThrownBy(() ->
postService.createPost("제목", tooLongContent, List.of()))
.isInstanceOf(ContentTooLongException.class);
}
@Test
void 게시물_작성_성공시_팔로워에게_알림이_발송된다() {
// given
String title = "제목";
String content = "내용";
List<String> images = List.of("image1.jpg");
// when
Post savedPost = postService.createPost(title, content, images);
// then
verify(notificationService).notifyFollowers(savedPost);
assertThat(savedPost.getTitle()).isEqualTo(title);
assertThat(savedPost.getContent()).isEqualTo(content);
}
서비스 테스트를 먼저 작성하면 다음과 같은 비즈니스 규칙들을 먼저 검증할 수 있다:
1. 이미지는 최대 5장까지만 첨부 가능하다
2. 게시물 내용은 500자를 넘을 수 없다
3. 게시물 작성 시 팔로워들에게 알림이 발송되어야 한다
(상위 항목 관련 예시 설명 추가)
1. 도메인 규칙 먼저 검증하기
2. API 스펙이 바뀌어도 비즈니스 로직 유지
3. 실제 사용자 시나리오에 집중
->그렇지만 "반드시" 따라야 하는 규칙은 아님.
프로젝트의 특성이나 팀의 상황에 따라 컨트롤러 테스트를 먼저 작성하는 것이 더 효율적일 수도 있으며, 특히 API 스펙이 먼저 확정되어야 하는 경우라면 컨트롤러 테스트를 먼저 작성하는 것이 유리할 수 있음.