[Test] Presentation Layer Test(with Spring Boot)

2025. 7. 5. 18:22·Software Development/Test

지금까지 Repository와 Service 계층을 통합 테스트하며 아래에서 위로 올라왔다. 드디어 외부 세계의 요청을 가장 먼저 마주하는 Presentation Layer(Controller) 에 도착했다.

Controller를 테스트할 때는 이전과 다른 전략을 사용한다. 하위 계층(Service)은 이미 충분히 테스트했고 잘 동작한다고 가정하고, Controller의 책임에만 집중하는 것이다. 이를 위해 Mocking을 사용한다.

Controller 테스트와 Mocking

Mock은 말 그대로 '가짜' 객체다. Controller가 의존하는 Service를 가짜 객체로 대체하면, 우리는 Service의 실제 로직과 상관없이 Controller의 동작(요청 매핑, 파라미터 검증 등)만을 순수하게 테스트할 수 있다.

스프링 부트에서는 이를 위해 MockMvc와 @WebMvcTest를 제공한다.

  • @WebMvcTest(controllers = ...): Controller 관련 빈만 로드하는 가벼운 테스트 애노테이션.
  • @MockBean 또는 @MockitoBean: 진짜 Service 대신 컨테이너에 등록될 가짜(Mock) Service 객체.
  • MockMvc: 가짜 HTTP 요청을 만들어 컨트롤러를 테스트할 수 있게 해주는 도구.

요구사항: 관리자 페이지에서 신규 상품을 등록한다. (이름, 타입, 가격 등)

// ProductControllerTest.java
@WebMvcTest(controllers = ProductController.class)
class ProductControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper; // 요청 Body를 만들기 위한 직렬화 도구

    @MockitoBean // ProductService를 가짜 객체로 만든다.
    private ProductService productService;

    @DisplayName("신규 상품을 등록한다.")
    @Test
    void createProduct() throws Exception {
        // given
        ProductCreateRequest request = ProductCreateRequest.builder()
                .type(HANDMADE)
                .sellingStatus(SELLING)
                .name("아메리카노")
                .price(4000)
                .build();

        // when & then
        mockMvc.perform(
                post("/api/v1/products/new")
                        .content(objectMapper.writeValueAsString(request)) // 요청 Body
                        .contentType(MediaType.APPLICATION_JSON)
                )
                .andDo(print()) // 요청/응답 전체 내용 확인
                .andExpect(status().isOk()); // HTTP 상태 검증
    }
}

Controller의 진짜 책임: 유효성 검증(Validation)

Controller의 가장 중요한 역할 중 하나는 외부에서 들어온 파라미터가 유효한지 검사하는 것이다. Spring Validation을 사용해 이 부분을 테스트해보자.

// ProductCreateRequest.java
public class ProductCreateRequest {
    @NotNull(message = "상품 타입은 필수입니다.")
    private ProductType type;

    @NotBlank(message = "상품 이름은 필수입니다.")
    private String name;

    @Positive(message = "상품 가격은 양수여야 합니다.")
    private int price;
    // ...
}

이제 이 검증 로직이 잘 동작하는지 테스트한다.

// ProductControllerTest.java
@DisplayName("신규 상품을 등록할 때 상품 가격은 양수여야 한다.")
@Test
void createProductWithZeroPrice() throws Exception {
    // given
    ProductCreateRequest request = ProductCreateRequest.builder()
            .type(HANDMADE)
            .sellingStatus(SELLING)
            .name("아메리카노")
            .price(0) // 잘못된 값
            .build();

    // when & then
    mockMvc.perform(
            post("/api/v1/products/new")
                    .content(objectMapper.writeValueAsString(request))
                    .contentType(MediaType.APPLICATION_JSON)
            )
            .andDo(print())
            .andExpect(status().isBadRequest()) // 400 에러를 기대
            .andExpect(jsonPath("$.code").value("400")) // 응답 Body의 필드 값 검증
            .andExpect(jsonPath("$.message").value("상품 가격은 양수여야 합니다."));
}

이렇게 MockMvc를 사용하면 Service의 복잡한 로직과 상관없이 Controller의 책임인 '요청을 받고, 검증하고, 올바른 응답을 내려주는지'에만 집중해서 테스트할 수 있다.

Ref) Practical Testing: 실용적인 테스트 가이드 강의 | 박우빈 - 인프런

'Software Development > Test' 카테고리의 다른 글

[Test] Mock(with Mockito)  (0) 2025.07.05
[Test] Business Layer Test(with Spring Boot)  (0) 2025.07.05
[Test] Persistence Layer Test(with Spring Boot, JPA)  (0) 2025.07.05
[Test] 테스트는 '문서'다.  (0) 2025.07.05
[Test] TDD(Test Driven Development)  (0) 2025.07.05
'Software Development/Test' 카테고리의 다른 글
  • [Test] Mock(with Mockito)
  • [Test] Business Layer Test(with Spring Boot)
  • [Test] Persistence Layer Test(with Spring Boot, JPA)
  • [Test] 테스트는 '문서'다.
lumana
lumana
배움을 나누는 공간 https://github.com/bebeis
  • lumana
    Brute force Study
    lumana
  • 전체
    오늘
    어제
    • 분류 전체보기 (457)
      • Software Development (27)
        • Performance (0)
        • TroubleShooting (1)
        • Refactoring (0)
        • Test (8)
        • Code Style, Convetion (0)
        • DDD (0)
        • Software Engineering (18)
      • Java (71)
        • Basic (5)
        • Core (21)
        • Collection (7)
        • 멀티스레드&동시성 (13)
        • IO, Network (8)
        • Reflection, Annotation (3)
        • Modern Java(8~) (12)
        • JVM (2)
      • Spring (53)
        • Framework (12)
        • MVC (23)
        • Transaction (3)
        • AOP (11)
        • Boot (0)
        • AI (0)
      • DB Access (1)
        • Jdbc (1)
        • JdbcTemplate (0)
        • JPA (14)
        • Spring Data JPA (0)
        • QueryDSL (0)
      • Computer Science (130)
        • Data Structure (27)
        • OS (14)
        • Database (10)
        • Network (21)
        • 컴퓨터구조 (6)
        • 시스템 프로그래밍 (23)
        • Algorithm (29)
      • HTTP (8)
      • Infra (1)
        • Docker (1)
      • 프로그래밍언어론 (15)
      • Programming Language(Sub) (77)
        • Kotlin (1)
        • Python (25)
        • C++ (51)
        • JavaScript (0)
      • FE (11)
        • HTML (1)
        • CSS (9)
        • React (0)
        • Application (1)
      • Unix_Linux (0)
        • Common (0)
      • PS (13)
        • BOJ (7)
        • Tip (3)
        • 프로그래머스 (0)
        • CodeForce (0)
      • Book Review (4)
        • Clean Code (4)
      • Math (3)
        • Linear Algebra (3)
      • AI (7)
        • DL (0)
        • ML (0)
        • DA (0)
        • Concepts (7)
      • 프리코스 (4)
      • Project Review (6)
      • LegacyPosts (11)
      • 모니터 (0)
      • Diary (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
lumana
[Test] Presentation Layer Test(with Spring Boot)
상단으로

티스토리툴바