[JAVA] MockMVC

SEOP·2024년 12월 21일
post-thumbnail

1. MockMVC란 무엇인가?

MockMVC는 Spring Framework에서 제공하는 테스트 도구로, 웹 애플리케이션의 MVC (Model-View-Controller) 구성 요소를 테스트하는 데 사용된다.
실제 서버를 실행하지 않고도 컨트롤러의 동작을 검증할 수 있기 때문에, 효율적이고 빠른 테스트가 가능하다.

2. MockMVC 주요 특징

2.1 서버 없이 테스트 가능

MockMVC는 실제 HTTP 서버를 실행하지 않고도 요청을 보내고 응답을 받을 수 있게 해준다.
이는 테스트 속도를 높이고, 복잡성을 줄여준다.

2.2 HTTP 요청 시뮬레이션

HTTP GET, POST, PUT, DELETE와 같은 다양한 요청을 시뮬레이션할 수 있다.
이를 통해 컨트롤러가 올바르게 작동하는지 확인할 수 있다.

2.3 응답 검증 기능 제공

MockMVC를 사용하여 응답의 상태 코드, 콘텐츠 타입, 응답 본문 등을 검증할 수 있다.
이를 통해 예상한 결과와 실제 결과를 비교할 수 있다.

2.4 스프링 통합 용이

Spring의 DI(Dependency Injection)와 AOP(Aspect-Oriented Programming)와 잘 통합되어, 복잡한 설정 없이도 쉽게 테스트할 수 있다.

3. MockMVC 장점

  • 테스트 속도 증가로 인한 개발 효율성 향상
  • 리소스 절약 (실제 서버 필요 없음)
  • 테스트의 신뢰성 향상 (모든 경로와 예외 처리 검증 가능)
  • 지속적 통합 및 배포(CI/CD) 환경에 적합

4. MockMVC 예시

4.1. Service

package org.mockmvc.mockmvctest.service;

import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    public List<String> getAllUsers() {
        return List.of("User1", "User2", "User3");
    }
}

4.2 Controller

package org.mockmvc.mockmvctest.controller;

import org.mockmvc.mockmvctest.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/users")
    public List<String> getUsers() {
        return userService.getAllUsers();
    }
}

4.3 ControllerTest

package org.mockmvc.mockmvctest.controller;

import org.junit.jupiter.api.Test;
import org.mockmvc.mockmvctest.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

import java.util.List;

import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

@WebMvcTest(UserController.class)
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean // UserService를 모킹
    private UserService userService;

    @Test
    public void testGetUsers() throws Exception {
        // UserService의 getAllUsers() 메서드가 호출될 때 반환할 값을 설정합니다.
        given(userService.getAllUsers()).willReturn(List.of("User1", "User2", "User3"));

        // MockMvc를 사용하여 GET 요청을 수행하고, 응답을 검증합니다.
        mockMvc.perform(get("/users"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0]").value("User1"))
                .andExpect(jsonPath("$[1]").value("User2"))
                .andExpect(jsonPath("$[2]").value("User3"));
    }
}

4.4 테스트 결과

테스트가 잘 통과한 것을 확인할 수 있다.

4.5 테스트 실패 예시

package org.mockmvc.mockmvctest.controller;

import org.junit.jupiter.api.Test;
import org.mockmvc.mockmvctest.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

import java.util.List;

import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

@WebMvcTest(UserController.class)
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean // UserService를 모킹
    private UserService userService;

    @Test
    public void testGetUsers() throws Exception {
        // UserService의 getAllUsers() 메서드가 호출될 때 반환할 값을 설정합니다.
        given(userService.getAllUsers()).willReturn(List.of("User1", "User2", "User3"));

        // MockMvc를 사용하여 GET 요청을 수행하고, 응답을 검증합니다.
        mockMvc.perform(get("/users"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0]").value("UNKNOWN")) // 테스트 실패 확인을 위해 변경
                .andExpect(jsonPath("$[1]").value("User2"))
                .andExpect(jsonPath("$[2]").value("User3"));
    }
}

테스트 실패를 확인하기 위해 perform() 메서드 체이닝 중 andExpect()User1 에서 UNKNOWN 으로 변경해보았다.

이미지를 보면 알 수 있듯이, 예상되는 결과와 실제 결과가 다르기에 테스트 실패를 알려준다.

5. Mocking을 테스트 하며...

given(userService.getAllUsers()).willReturn(List.of("User1", "User2", "User3"));


userService.getAllUsers() 메서드를 호출하면 List.of("User1", "User2", "User3") 를 반환받도록 명시해주었는데 TEST코드에서 왜 반환되는 결과를 다시 적어주는가?

@MockBean 어노테이션을 사용하여 UserService의 Mock 객체를 생성하면, 해당 Mock 객체는 실제 서비스 클래스의 구현과는 별개로 동작한다.
즉, Mock 객체는 실제 구현을 대체하여, 특정 동작을 정의하고 이를 테스트에 사용할 수 있게 해준다.

만약 내가 given(userService.getAllUsers()).willReturn(List.of("User4", "User5")); 처럼 서비스 반환 값과 다르게 하더라도 정상적으로 작동한다.
* 다만, 반환 타입은 동일하게 해줘야 한다.

profile
응애 나 애기 개발자

0개의 댓글