해당 글에서는 MockMvc에 대해 이해하고 활용하는 방법에 대해 확인해 봅니다.
💡 [참고] 이전에 작성한 Test 관련 글들을 읽으시면 도움이 됩니다.
분류 | 링크 |
JUnit 5 이론 및 구성 요소 | https://adjh54.tistory.com/341 |
JUnit 5 환경구성 및 활용예제 | https://adjh54.tistory.com/342 |
JUnit 5 + Mockito 이론 및 활용예제 | https://adjh54.tistory.com/346 |
JUnit 5 + MockMvc 이론 및 활용예제 | https://adjh54.tistory.com/347 |
Assertions API Document | https://adjh54.tistory.com/348 |
개발방법론 TDD, BDD | https://adjh54.tistory.com/305 |
JUnit 5 + Mockito + MockMVC 사용예제 Github | https://github.com/adjh54ir/blog-codes/tree/main/spring-boot-test |
1) MockMvc
💡 MockMvc
- 스프링 프레임워크에서 제공하는 웹 애플리케이션 테스트용 라이브러리를 의미합니다. 이를 사용하면 웹 애플리케이션의 다양한 컴포넌트를 테스트할 수 있습니다.
- MockMvc를 사용하면 HTTP 요청을 작성하고 컨트롤러의 응답을 검증할 수 있습니다. 이를 통해 통합 테스트를 실행하지 않고도 컨트롤러의 동작을 확인할 수 있습니다.
1. MockMvc를 이용한 테스트 목적
💡 MockMvc를 이용한 테스트 목적
- MockMvc를 이용하여 컨트롤러의 동작을 테스트하는 데 사용됩니다.
- 컨트롤러의 엔드포인트를 호출하여 HTTP 클라이언트의 요청을 모방하고 적절한 응답을 확인하기 위해 테스트를 수행합니다.
- 이러한 테스트 과정을 통해 애플리케이션의 서비스 로직이나 API 엔드포인트가 의도한 대로 동작하는지 확인하고, 버그를 발견하고 수정하는 데 도움을 주는 것입니다.
2. MockMvc를 이용한 Controller내의 흐름
💡 MockMvc를 이용한 Controller내의 흐름
1. TestCase → MockMvc
- TestCase 내에서 MockMvc 객체를 생성합니다. 이 객체는 테스트할 컨트롤러와 상호작용을 하는 데 사용이 됩니다.
2. MockMvc → TestDispatcher Servlet
- MockMvc를 사용하여 원하는 엔드포인트에 요청을 보냅니다. 또한 해당 요청에 필요한 파라미터, 헤더 또는 쿠키 등을 설정합니다.
- 예를 들어, GET 요청을 보내고 싶다면 perform(MockMvcRequestBuilders.get("/endpoint"))와 같이 요청을 설정합니다.
- 파라미터 설정은 param("paramName", "paramValue")와 같이 파라미터를 설정할 수 있습니다.
3. TestDispatcher Servlet → Controller
- 요청을 실행하고 응답을 받습니다. andExpect 메서드를 사용하여 응답의 상태코드, 헤더, 본문 등을 검증할 수 있습니다.
4. MockMvc → TestCase
- 필요한 검증을 추가합니다.
- 예를 들어, 응답 본문의 내용을 검증하고 싶다면 andExpect(content(). string("expectedValue"))와 같이 검증을 추가합니다.

2) MockMvc, ResultActions, MvcResult 주요 메서드
1. MockMvc
💡 MockMvc
- 스프링 프레임워크에서 제공하는 테스트용 라이브러리입니다. 이 라이브러리를 사용하면 Spring MVC 컨트롤러의 단위 테스트를 쉽게 작성할 수 있습니다.
- MockMvc를 사용하면 HTTP 요청을 작성하고 컨트롤러의 응답을 검증할 수 있습니다. 이를 통해 통합 테스트를 실행하지 않고도 컨트롤러의 동작을 확인할 수 있습니다.
메서드 | 설명 |
standaloneSetup() | 특정 컨트롤러를 MockMvc에 설정하여 테스트할 수 있는 환경을 구성합니다. |
perform() | MockMvc를 사용하여 HTTP 요청을 실행합니다. |
andExpect() | 컨트롤러의 응답을 검증합니다. |
andExpect(status().isOk()) | 응답 상태 코드가 200인지 확인합니다. |
andExpect(content().string("expected")) | 응답 본문의 내용이 "expected"인지 확인합니다. |
andExpect(jsonPath("$.property").value("expected")) | JSON 응답에서 특정 속성의 값이 "expected"인지 확인합니다. |
andExpect(view().name("expectedView")) | 응답에 대한 뷰의 이름이 "expectedView"인지 확인합니다. |
andExpect(model().attribute("attributeName", "expectedValue")) | 모델 속성의 값이 "expectedValue"인지 확인합니다. |
andExpect(redirectedUrl("expectedUrl")) | 리다이렉트된 URL이 "expectedUrl"인지 확인합니다. |
MockMvc (Spring Framework 6.1.2 API)
Perform a request and return a type that allows chaining further actions, such as asserting expectations, on the result.
docs.spring.io
💡 MockMvc 메서드를 이용한 사용예시
@ExtendWith(MockitoExtension.class) // Junit5 - Mockito 연동
class CodeControllerTest {
private MockMvc mockMvc; // HTTP 호출을 위한 MockMVC 사용
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(codeController).build();
}
@Test
public void testExample() throws Exception {
mockMvc.perform(get("/example")) // "/example"로 GET 요청 수행
.andExpect(status().isOk()) // 상태 코드 200인 성공적인 응답을 기대합니다
.andExpect(content().string("expected")) // 응답 내용이 "expected"와 같을 것으로 기대합니다
.andExpect(jsonPath("$.property").value("expected")) // JSON 속성 "property"의 값이 "expected"와 같을 것으로 기대합니다
.andExpect(view().name("expectedView")) // 뷰 이름이 "expectedView"와 같을 것으로 기대합니다
.andExpect(model().attribute("attributeName", "expectedValue")) // 모델 속성 "attributeName"의 값이 "expectedValue"와 같을 것으로 기대합니다
.andExpect(redirectedUrl("expectedUrl")); // "expectedUrl"로의 리다이렉트를 기대합니다
}
}
2. ResultActions
💡 ResultActions
- MockMvc를 사용하여 실행한 ‘HTTP 요청에 대한 결과’를 나타냅니다. 이를 통해 컨트롤러의 응답을 검증하고 원하는 동작을 수행할 수 있습니다.
메서드 | 설명 |
andReturn() | 해결된 MvcResult 객체를 반환합니다. |
andReturn(MvcResult) | 반환할 MvcResult를 설정합니다. |
andDo(ResultHandler) | 결과에 대해 추가 작업을 수행합니다. |
andDo(ResultMatcher) | 결과에 ResultMatcher를 추가합니다. |
andExpect(ResultMatcher) | 결과에 대한 기대치로 ResultMatcher를 추가합니다. |
andForward() | 요청을 다음 핸들러로 전달합니다. |
andForward(String) | 요청을 지정된 URL로 전달합니다. |
andExpect(MockMvcResultMatchers) | MockMvcResultMatchers에서 ResultMatcher를 추가합니다. |
andExpect(MockMvcResultHandlers) | MockMvcResultHandlers에서 ResultHandler를 추가합니다. |
andReverse() | 이전 전달을 뒤집습니다. |
andForwardDefault() | 요청을 기본 핸들러로 전달합니다. |
andForwardDefault(String) | 요청을 지정된 URL로 기본 핸들러로 전달합니다. |
andReturnDefault() | 기본 핸들러에 대한 해결된 MvcResult 객체를 반환합니다. |
andReturnDefault(MvcResult) | 기본 핸들러에 반환할 MvcResult를 설정합니다. |
andDoDefault() | 기본 핸들러에 대해 추가 작업을 수행합니다. |
andDoDefault(ResultHandler) | 지정된 핸들러를 사용하여 기본 핸들러에 대해 추가 작업을 수행합니다. |
andDoDefault(ResultMatcher) | 기본 핸들러에 ResultMatcher를 추가합니다. |
andExpectDefault(ResultMatcher) | 기본 핸들러에 대한 결과 기대치로 ResultMatcher를 추가합니다. |
andForwardNamed(String) | 지정된 URL로 명명된 핸들러로 요청을 전달합니다. |
andReturnNamed(String) | 명명된 핸들러에 대한 해결된 MvcResult 객체를 반환합니다. |
andDoNamed(String) | 명명된 핸들러에서 추가 작업을 수행합니다. |
andDoNamed(String, ResultHandler) | 지정된 핸들러를 사용하여 명명된 핸들러에서 추가 작업을 수행합니다. |
andDoNamed(String, ResultMatcher) | 명명된 핸들러에 ResultMatcher를 추가합니다. |
andExpectNamed(String, ResultMatcher) | 명명된 핸들러에 대한 결과 기대치로 ResultMatcher를 추가합니다. |
andReverseNamed(String) | 이전 명명된 핸들러로의 전달을 뒤집습니다. |
andReverseDefault() | 기본 핸들러로의 이전 전달을 뒤집습니다. |
ResultActions (Spring Framework 6.1.2 API)
Perform multiple expectations, with the guarantee that all expectations will be asserted even if one or more expectations fail with an exception.
docs.spring.io
💡 ResultActions 메서드 사용 예시
- SampleTest 클래스는 테스트를 수행하는 메서드인 sampleTest()를 가지고 있습니다.
- 이 메서드에서는 /api/sample 엔드포인트에 대한 GET 요청을 수행하고, 응답의 상태 코드가 200 (OK)이며, JSON 응답의 message 필드가 "Hello, World!"인지를 검증합니다.
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
public class SampleTest {
private MockMvc mockMvc;
// ...
public void sampleTest() throws Exception {
ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/api/sample"));
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("Hello, World!"));
// Add more assertions or actions as needed
}
}
3. MvcResult
💡 MvcResult
- MockMvc에서 수행된 MVC 요청의 결과에 대한 상세한 정보를 제공합니다. 이 클래스는 응답 상태, 헤더, 내용 등과 같은 정보를 추출하기 위한 다양한 메서드를 포함하고 있습니다.
메서드 | 설명 |
getModelAndView() | 응답에 대한 모델과 뷰를 담고 있는 ModelAndView 객체를 반환합니다. |
getRequest() | 응답과 관련된 HttpServletRequest 객체를 반환합니다. |
getResponse() | 응답과 관련된 HttpServletResponse 객체를 반환합니다. |
getResponseHeaders() | 응답 헤더를 맵 형태로 반환합니다. |
getResponseStatus() | 응답 상태 코드를 반환합니다. |
getModel() | 응답으로부터 모델 객체를 반환합니다. |
getModelAndViewName() | ModelAndView 객체로부터 뷰의 이름을 반환합니다. |
MvcResult (Spring Framework 6.1.2 API)
Get the result of async execution. This method will wait for the async result to be set within the timeout value associated with the async request, see MockAsyncContext#setTimeout. Alternatively, use getAsyncResult(long) to specify the amount of time to wa
docs.spring.io
💡 MvcResult 메서드 사용 예시
mvcResult.getModelAndView().getModel(); // ModelAndView 객체에서 모델을 가져옵니다.
mvcResult.getModelAndView().getModelAndViewName(); // ModelAndView 객체의 이름을 가져옵니다.
mvcResult.getRequest(); // 요청 객체를 가져옵니다.
mvcResult.getResponse(); // 응답 객체를 가져옵니다.
mvcResult.getResponseHeaders(); // 응답 헤더를 가져옵니다.
mvcResult.getResponseStatus(); // 응답 상태를 가져옵니다.
3) MockMvc에서 사용되는 어노테이션
💡 MockMvc에서 사용되는 어노테이션
- 해당 어노테이션은 JUnit5 어노테이션과 Mockito 어노테이션을 포함하였으며 테스트를 진행할 때 주로 사용되는 어노테이션 위주로 구성하였습니다.
어노테이션 | 설명 |
@ExtendWith(MockitoExtension.class) | Mockito를 사용하여 모킹하기 위해 테스트 클래스에 적용됩니다. |
@WebMvcTest | 웹 MVC 테스트를 위해 스프링 컨텍스트를 구성합니다. |
@AutoConfigureJsonTesters | JSON 테스트를 위해 JsonTester의 자동 구성을 활성화합니다. |
@AutoConfigureMockMvc | MockMvc를 자동으로 구성하는 데 사용됩니다. |
@Mock | 모킹 대상 객체를 생성하여 주입합니다. |
@MockBean | Spring 컨텍스트에서 Mock 객체를 생성하여 주입합니다. |
@SpringBootTest | 스프링 부트 애플리케이션의 통합 테스트를 위해 스프링 컨텍스트를 구성합니다. |
@BeforEach | 각각의 테스트 메서드가 실행되기 전에 실행되는 메서드를 지정합니다. |
@Test | 테스트 메서드를 지정합니다. |
4) 초기환경 구성
1. 의존성 주입
💡 의존성 주입
- MockMVC를 사용하기 위해서는 ‘spring-boot-starter-test’ 라이브러리를 추가해야 합니다. 또한 Mockito 라이브러리 기능도 함께 사용하기에 의존성을 추가합니다.
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test' // Spring Boot Starter Test
testImplementation 'org.mockito:mockito-core:5.8.0' // Mockito Core
}
2. Controller 테스트를 위한 파일 생성: Go to… - Test 선택

3. 테스트 생성

4. 테스트 생성 파일 확인

5) MockMvc 단계 별 구성 과정 : Controller 테스트
1. Mock 초기화
💡 Mock 초기화
- MockMvc에서도 역시 Mock 객체를 초기화하는 데 사용됩니다. 이를 수행하면 테스트에서 Mock 객체를 사용할 수 있고 테스트를 실행할 때 예상된 동작을 가진 Mock 객체를 사용할 수 있습니다.
어노테이션 | 사용 버전 | 설명 |
@ExtendWith(MockitoExtension.class) | JUnit 5 | - Mockito를 사용하여 테스트 클래스를 초기화하는 데 사용됩니다. - MockitoExtension은 JUnit 5의 확장 기능으로, Mockito의 기능을 테스트 클래스에 적용합니다. |
💡 Mock는 Mockito와 함께 사용되므로 초기화를 수행합니다.
@ExtendWith(MockitoExtension.class)
class CodeControllerTest {
}
2. Mock Annotation & MockMvc 구성
💡Mock Annotation & MockMvc 구성
- MockMvc는 Controller를 테스트하는 데 사용되기에 Controller에 @InjectMocks으로 객체를 생성하고 인스턴스를 주입합니다.
1. @InjectMocks Controller
- @InjectMocks으로 객체를 생성하고 인스턴스를 주입합니다.
2. private MockMvc mockMvc
- MockMvc를 선언합니다.
3. @BeforeEach
- 선언한 MockMvc에 MockMvcBuilders.standaloneSetup(Class)를 통해 @Test가 실행되기 전에 독립적인 클래스(Controller)를 위해 선언하고 인스턴스를 생성하기 위해 구성합니다.
어노테이션 | 설명 |
@Mock | 모의 객체(Mock Object)를 생성하는데 사용됩니다. |
@InjectMocks | 모의 객체를 생성하고 인스턴스를 주입하는데 사용됩니다. |
@ExtendWith(MockitoExtension.class)
class CodeControllerTest {
@InjectMocks
private CodeController codeController;
private MockMvc mockMvc;
}
3. MockMvcBuilders.standaloneSetup(Class) 구성
💡 MockMvcBuilders.standaloneSetup(Class) 구성
- 선언한 MockMvc에 MockMvcBuilders.standaloneSetup(Class) 를 통해 @Test가 실행되기 전에 독립적인 클래스(Controller)를 위해 선언하고 인스턴스를 생성하기 위해 구성합니다.
@ExtendWith(MockitoExtension.class)
class CodeControllerTest {
@InjectMocks
private CodeController codeController;
private MockMvc mockMvc;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(codeController).build();
}
}
4. MockMvc 테스트 방식

테스트 방식 | 설명 |
Standalone 테스트 | 독립적으로 테스트를 수행한다는 것을 의미합니다. 다른 의존성 없이 테스트를 실행할 수 있으며, 외부 리소스나 서버에 대한 연결 없이 테스트를 수행할 수 있습니다. |
Spring Context를 수행하여 테스트 | Spring Context를 실행하여 테스트를 수행합니다. 이는 의존성 주입과 같은 Spring의 기능을 사용할 수 있게 해줍니다. |
Web Server를 수행하여 테스트 | Web Server를 실행하여 테스트를 수행합니다. 이는 실제 서버에 대한 테스트를 가능하게 해주며, 외부 리소스와의 상호작용을 테스트할 수 있습니다. |
6) MockMvc 활용 예제 -1: 웹 서버 없이 테스트
1. 테스트 활용 구조
💡 해당 테스트에서는 ‘웹 서버’와 Spring Context 수행 없이 컨트롤러 로직을 테스트하는 경우입니다.

2. Controller 테스트 구조 및 사용 예시
💡 Controller 테스트 구조
- 해당 테스트를 수행할 구조는 Controller에서 수행됩니다.
- 주로 엔드포인트에 따른 요청에 따른 응답 값을 반환하고 있는지를 테스트합니다. 그렇기에 특정 API 엔드포인트에 요청 값을 담아서 보내면 응답값으로 전달이 되는지에 대해 확인합니다.
package com.adjh.multiflexapi.controller;
import com.adjh.multiflexapi.common.codes.SuccessCode;
import com.adjh.multiflexapi.common.response.ApiResponse;
import com.adjh.multiflexapi.model.CodeDto;
import com.adjh.multiflexapi.service.CodeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 코드 정보를 관리하는 Controller
*/
@Slf4j
@RestController
@RequestMapping(value = "/api/v1/code")
@Tag(name = "Code", description = "코드 API")
public class CodeController {
private final CodeService codeService;
public CodeController(CodeService codeService) {
this.codeService = codeService;
}
/**
* [API] 코드 리스트 출력
*
* @param codeDto codeDto
* @return ApiResponseWrapper<ApiResponse> : 응답 결과 및 응답 코드 반환
*/
@PostMapping("/codes")
@Operation(summary = "코드 조회", description = "코드 조회")
public ResponseEntity<ApiResponse<Object>> selectCodeList(@RequestBody @Validated CodeDto codeDto) {
log.debug("코드의 모든 리스트를 조회합니다.");
ApiResponse<Object> ar = ApiResponse.builder()
.result(codeDto)
.resultCode(SuccessCode.SELECT.getStatus())
.resultMsg(SuccessCode.SELECT.getMessage())
.build();
return new ResponseEntity<>(ar, HttpStatus.OK);
}
/**
* [API] 코드 단건 조회 : 코드 키 값을 기반으로 조회합니다.
*
* @param cd String
* @return ApiResponseWrapper<ApiResponse> : 응답 결과 및 응답 코드 반환
*/
@GetMapping("/code")
@Operation(summary = "코드 값 별 코드 조회", description = "코드 조회")
public ResponseEntity<ApiResponse<Object>> selectCodeByCd(@RequestParam String cd) {
CodeDto codeItem = codeService.selectCodeByCd(cd);
ApiResponse<Object> ar = ApiResponse.builder()
.result(codeItem)
.resultCode(SuccessCode.SELECT.getStatus())
.resultMsg(SuccessCode.SELECT.getMessage())
.build();
return new ResponseEntity<>(ar, HttpStatus.OK);
}
}
💡 위에 Restful api를 기반으로 CodeControllerTest.java 파일을 자동생성하여 아래와 같이 구성하였습니다.
- 자동생성 방법은 위에 환경 설정 부분을 참고하시면 됩니다.

2.1. Controller : 조회 테스트(요청 파라미터 전달)
💡 Controller : 조회 테스트
- 조회는 selectCodeList() 객체를 받아서 전체를 조회하는 메서드와 selectCodeByCd() String 문자열 키를 받아서 조회하는 메서드에 대해 테스트를 진행합니다.
💡 사용예시
- "/api/v1/code/code" 엔드포인트에 대한 GET 요청을 수행하고, "java"라는 코드 파라미터를 전달하여 테스트하고 있습니다.
- 테스트 결과로는 MockMvc의 andExpect 메서드를 사용하여 상태코드가 200인지 확인하고, andDo 메서드를 사용하여 응답 내용을 출력하고 있습니다.
@Slf4j
@ExtendWith(MockitoExtension.class)
class CodeControllerTest {
@InjectMocks
private CodeController codeController;
@Mock
private CodeService codeService;
private MockMvc mockMvc;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(codeController).build();
}
@Test
@DisplayName("코드 단건을 조회합니다.")
void selectCodeByCd() throws Exception {
// given
String paramCd = "java";
// when
ResultActions resultActions = mockMvc.perform(
MockMvcRequestBuilders.get("/api/v1/code/code")
.param("cd", paramCd)
.contentType(MediaType.APPLICATION_JSON));
// then
MvcResult mvcResult = resultActions
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println("mvcResult :: " + mvcResult.getResponse().getContentAsString());
}
}

💡 [참고] 필수 파라미터를 전달하지 않은 경우

💡 [참고] @Mock CodeService codeService;를 선언하고 사용하지 않는데 아래와 같은 오류가 발생하는 이유는?
- codeService가 null인 상태에서 CodeController의 selectCodeByCd 메서드를 호출할 때 NullPointerException이 발생하기 때문입니다.
- codeService는 CodeController의 의존성으로 주입되어야 하지만, codeService가 없는 상태로 테스트를 진행하면 codeService가 null로 초기화되어 있어서 해당 메서드를 호출할 수 없기 때문에 오류가 발생합니다.

2.2. Contoller : 조회 테스트(객체 전달)
💡 사용예시
- "/api/v1/code/codes" 엔드포인트에 대한 POST 요청을 수행하고, 객체를 전달하여 테스트하고 있습니다.
- 테스트 결과로는 MockMvc의 andExpect 메서드를 사용하여 상태코드가 200인지 확인하고, andDo 메서드를 사용하여 응답 내용을 출력하고 있습니다.
@Slf4j
@ExtendWith(MockitoExtension.class)
class CodeControllerTest {
@InjectMocks
private CodeController codeController;
@Mock
private CodeService codeService;
private MockMvc mockMvc;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(codeController).build();
}
@Test
@DisplayName("코드 전체 리스트를 조회합니다.")
void selectCodeListTest() throws Exception {
// given
CodeDto codeDto = CodeDto.builder().cd("java").build();
// when
ResultActions resultActions = mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/code/codes")
.contentType(MediaType.APPLICATION_JSON)
.content(new Gson().toJson(codeDto)));
// then
MvcResult mvcResult = resultActions
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println("mvcResult :: " + mvcResult.getResponse().getContentAsString());
}
}

7) MockMvc 활용 예제 -2: Spring Context를 사용하여 테스트
1. 테스트 활용 구조
💡 테스트 활용 구조
- 해당 테스트 이전 구조에서는 다르게 Web Server를 직접적으로 사용하지 않지만 ‘Spring Context’까지 사용하여 테스트를 진행하는 방법입니다.
- 해당 방식은 @WebMVCTest 어노테이션을 통해 MockMvc 인스턴스가 자동으로 구성되고 컨텍스트에서 사용이 가능해집니다.

[ 더 알아보기 ]
💡 웹 서버(Web Server)
- 클라이언트의 요청을 받아들이고, 처리한 결과를 클라이언트에게 전달하는 역할을 수행하는 소프트웨어입니다.
- 웹 서버는 HTTP 프로토콜을 사용하여 클라이언트와 통신하며, 주로 웹 애플리케이션을 호스팅 하고 실행하는 역할을 합니다.
💡 스프링 컨텍스트(Spring Context)
- 스프링 프레임워크에서 제공하는 기능 중 하나로, 애플리케이션의 구성 요소들을 관리하고 제어하는 역할을 합니다.
- 스프링 컨텍스트는 애플리케이션의 객체들을 생성하고 관리하며, 의존성 주입(Dependency Injection)을 통해 객체 간의 관계를 설정합니다.
💡 해당 테스트 구조를 본다면 웹 서버와 스프링 컨텍스트의 독립적인 관계로 이해하면 되나?
- 각각의 기능으로 봤을 때 스프링 컨텍스트는 웹 애플리케이션의 객체 관리와 제어에 사용되는 독립적인 컨테이너이며, 웹 서버는 클라이언트의 요청을 받아들이고 처리한 결과를 전달하는 역할을 수행합니다.
- 따라서, 스프링 컨텍스트와 웹 서버는 서로 다른 역할을 가지고 있으며, 독립적으로 실행될 수 있습니다.
2. Controller 테스트 구조 및 사용 예시
💡 사용 예시
-
해당 테스트에서는 @WebMvcTest(CodeController.class)를 통해서 슬라이스 테스트를 진행하였습니다.
- 이를 통해 Spring Context를 실행하여 테스트를 하며 이전에 standalone 방식을 사용하지 않고 수행하였습니다.
package com.adjh.multiflexapi.controller;
import com.adjh.multiflexapi.model.CodeDto;
import com.adjh.multiflexapi.service.CodeService;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* MockMvc 활용하여 Spring Context를 실행시켜 테스트
*
* @author : lee
* @fileName : CodeControllerSpringContext
* @since : 12/14/23
*/
@Slf4j
@AutoConfigureJsonTesters
@WebMvcTest(CodeController.class)
class CodeControllerSpringContext {
@MockBean
private CodeController codeController;
@Mock
private CodeService codeService;
@Autowired
private MockMvc mockMvc;
@BeforeEach
void setUp() {
}
@Test
@DisplayName("코드 단건을 조회합니다.")
void selectCodeByCd() throws Exception {
// given
String paramCd = "java";
// when
ResultActions resultActions = mockMvc.perform(
MockMvcRequestBuilders.get("/api/v1/code/code")
.param("cd", paramCd)
.contentType(MediaType.APPLICATION_JSON));
// then
MvcResult mvcResult = resultActions
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println("mvcResult :: " + mvcResult.getResponse().getContentAsString());
}
@Test
@DisplayName("코드 전체 리스트를 조회합니다.")
void selectCodeListTest() throws Exception {
// given
CodeDto codeDto = CodeDto.builder().cd("java").build();
// when
ResultActions resultActions = mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/code/codes")
.contentType(MediaType.APPLICATION_JSON)
.content(new Gson().toJson(codeDto)));
// then
MvcResult mvcResult = resultActions
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println("mvcResult :: " + mvcResult.getResponse().getContentAsString());
}
}
💡 아래와 같이 컨텍스트가 수행되었지만 ‘401 Unauthorized’ 에러가 발생한 것을 확인할 수 있습니다.
- 해당 문제가 발생한 이유는 해당 코드에서 @WebMvcTest(CodeController.class) 어노테이션이 사용되어 특정 컨트롤러만을 대상으로 하는 슬라이스 테스트를 수행하고 있기 때문입니다.
- 이러한 슬라이스 테스트는 보안 및 인증과 관련된 기능을 포함하지 않습니다. 따라서, 인증되지 않은 요청이 발생하게 되면 401 Unauthorized 오류가 반환됩니다.


8) MockMvc 활용 예제 -3 : 웹 서버를 사용하여 테스트
1. 테스트 활용 구조
💡 테스트 활용 구조
- 해당 구조에서는 이전 구조와 다르게 Spring Context까지 사용했던 부분에서 이어나가 웹 서버까지 사용하여 테스트를 진행하는 방법입니다.
- @SpringBootTest 어노테이션을 통해 실제 HTTP 서버로 테스트를 진행하는 방법입니다.

2. Controller 테스트 구조 및 사용 예시
💡 사용예시
- 해당 테스트에서는 @SpringBootTest 어노테이션을 통해서 Web Server를 수행하도록 테스트를 진행하였습니다.
- TestRestTemplate을 통해서 HTTP 요청을 보내고 응답받는 형태로 체크를 하는 형태로 수행하였습니다.
package com.adjh.multiflexapi.controller;
import com.adjh.multiflexapi.model.CodeDto;
import com.adjh.multiflexapi.service.CodeService;
import groovy.util.logging.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;
import org.springframework.test.web.servlet.MockMvc;
/**
* Web Server 수행시켜 테스트를 하는 경우
*
* @author : lee
* @fileName : CodeControllerWebServerTest
* @since : 12/14/23
*/
@Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class CodeControllerWebServerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private CodeService codeService;
@Autowired
private TestRestTemplate restTemplate;
@Test
@DisplayName("코드 단건을 조회합니다.")
void selectCodeByCd() throws Exception {
// given
String paramCd = "java";
// when
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer xxxxxxx");
// then
HttpEntity<String> entity = new HttpEntity<>(headers);
String url = String.format("/api/v1/code/code?cd=%s", paramCd);
ResponseEntity<CodeDto> codeDtoResult = restTemplate.exchange(url, HttpMethod.GET, entity, CodeDto.class);
Assertions.assertEquals(codeDtoResult.getStatusCode(), HttpStatus.OK);
System.out.println("codeDtoResult ::" + codeDtoResult);
}
@Test
@DisplayName("코드 전체 리스트를 조회합니다.")
void selectCodeListTest() throws Exception {
// given
CodeDto codeDto = CodeDto.builder().cd("java").build();
// when
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer xxxxxx");
// then
HttpEntity<CodeDto> entity = new HttpEntity<>(codeDto, headers);
ResponseEntity<CodeDto> codeDtoResult = restTemplate.exchange("/api/v1/code/codes", HttpMethod.POST, entity, CodeDto.class);
Assertions.assertEquals(codeDtoResult.getStatusCode(), HttpStatus.OK);
System.out.println("codeDtoResult ::" + codeDtoResult);
}
}
💡 사용 결과
- 아래와 같이 서버가 수행되고 TestRestTemplate을 통해서 HTTP 통신을 통해 호출이 되어 정상임을 테스트하였습니다.

💡 [참고] Spring Boot 환경에서 JUnit5를 이용하기 위한 관련 글들입니다.
분류 | URL |
JUnit 5 이론 및 구성 요소 | https://adjh54.tistory.com/341 |
JUnit 5 환경구성 및 활용예제 | https://adjh54.tistory.com/342 |
JUnit 5 + Mockito 이론 및 활용예제 | https://adjh54.tistory.com/346 |
JUnit 5 + MockMvc 이론 및 활용예제 | https://adjh54.tistory.com/347 |
Assertions API Document | https://adjh54.tistory.com/348 |
MockMvc, ResultActions, MvcResult API Document | https://adjh54.tistory.com/349 |
오늘도 감사합니다. 😀
'Java > Testing' 카테고리의 다른 글
[Java] Spring Boot JUnit5 이해하기 -3: @RepeatedTest, @ParameterizedTest를 이용한 반복 테스트 (0) | 2024.07.28 |
---|---|
[Java] Spring Boot Mockito 이해하기 : 테스트 흐름 및 사용예시 (0) | 2023.12.14 |
[Java] Spring Boot JUnit5 이해하기 -2 : 환경구성 및 활용 예제 (0) | 2023.12.08 |
[Java] Spring Boot JUnit5 이해하기 -1 : 이론 및 구조 (1) | 2023.12.07 |