MockMvc

๋ž ๋œจยท2025๋…„ 1์›” 10์ผ

๐Ÿ”Ž Overview

ย  ์ตœ๊ทผ, ํ…Œ์ŠคํŠธ์˜ ์ค‘์š”์„ฑ์„ ์—ฌ์‹คํžˆ ๋А๋ผ๊ณ  ์žˆ๋‹ค.
์›๋ž˜ ์ฒ˜์Œ์—๋Š” ํ…Œ์ŠคํŠธ ํŒŒ์ผ์„ ๋”ฐ๋กœ ์ƒ์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์ง€ ์•Š์•˜๋‹ค. ์ด์œ ๋Š” ๊ฐ„๋‹จํ–ˆ๋‹ค. ์ž˜ ์‚ฌ์šฉํ•  ์ค„ ๋ชฐ๋ž๊ธฐ ๋•Œ๋ฌธ์—.

ย  ์ตœ๊ทผ ํ•™์Šต์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜๋Š” ๋ฒ•์„ ์ฒด๋“ํ•˜๊ณ  ์žˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ @Autowired๋กœ ์‹ค์ œ ์„œ๋น„์Šค๋‚˜ ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ๊ธฐ๋Šฅ์ด ์ž˜ ์ž‘๋™ํ•˜๋Š”์ง€๋ฅผ ํ…Œ์ŠคํŠธํ•˜์˜€๊ณ , ์ดํ›„์—๋Š” MockMvc๋ฅผ ํ†ตํ•œ ์ปจํŠธ๋กค๋Ÿฌ ํ…Œ์ŠคํŠธ ๋˜ํ•œ ์ง„ํ–‰ํ•˜์˜€๋‹ค.

ย  ํ…Œ์ŠคํŠธ ์‚ฌ์šฉ๋ฒ•์ด ์กฐ๊ธˆ์”ฉ ์ต์ˆ™ํ•ด์ง€๋ฉด์„œ, TDD๋กœ ๊ธฐ์กด์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์žฌ๊ตฌํ˜„ํ•˜๋Š” ์‹ค์Šต์„ ์ง„ํ–‰ํ•˜๋ฉฐ ํ…Œ์ŠคํŠธ์™€ ์ข€ ๋” ์นœ์ˆ™ํ•ด์ง€๊ณ  ์žˆ๋Š” ์š”์ฆ˜์ด๋‹ค.

ย  ๊ฐœ์ธ์ ์œผ๋กœ ์ฒ˜์Œ์— ๋˜๊ฒŒ ์ƒ์†Œํ•˜๊ฒŒ ๋А๊ผˆ๋˜ ๊ฒƒ์ด ๋ฐ”๋กœ ์ปจํŠธ๋กค๋Ÿฌ ํ…Œ์ŠคํŠธ์ด๋ฉฐ, ์ด๋ฅผ MockMvc๋ฅผ ์ด์šฉํ•˜์—ฌ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค.
์ด MockMvc๊ฐ€ ๋ฌด์—‡์ด๋ฉฐ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๊ณ , ์–ด๋–ป๊ฒŒ ์‘๋‹ตํ•˜๋Š”์ง€ ์ •๋ฆฌํ•ด๋ณด๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ ธ๋ณด์•˜๋‹ค.


๐Ÿ“• MockMvc

  • ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ…Œ์ŠคํŠธํ•  ๋•Œ, Servlet Container , ์ฆ‰ ์‹ค์ œ ์„œ๋ฒ„๋ฅผ ๋„์šฐ์ง€ ์•Š๊ณ ๋„ ์ปจํŠธ๋กค๋Ÿฌ ๋ ˆ์ด์–ด๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋„๊ตฌ
  • Spring MVC ์˜ ์š”์ฒญ-์‘๋‹ต ํ๋ฆ„์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๋ฉฐ, ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์˜ˆ์ƒ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ณผ์ •
  • HTTP ์š”์ฒญ ๋ฐ ์‘๋‹ต์„ Mockingํ•ด์„œ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ๋‹จ์ˆœํ™”


๐Ÿ”Ž MockMvc๋ฅผ ์‚ฌ์šฉํ•œ ์ปจํŠธ๋กค๋Ÿฌ ํ…Œ์ŠคํŠธ์˜ ๋ชฉ์ 

1. ์š”์ฒญ-๋งคํ•‘ ๊ฒ€์ฆ

  • ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ URI ์š”์ฒญ์ด ์˜ฌ ๋•Œ, ์ด๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅธ Handler Method ๋กœ ๋งคํ•‘๋˜๋Š”์ง€ ํ™•์ธ
  • ex) /api/v1/member/login URI ๋ฅผ ์š”์ฒญ๋ฐ›์•˜์„ ๋•Œ, ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์„œ๋“œ๋กœ ์ž˜ ์—ฐ๊ฒฐ๋˜๋Š”๊ฐ€

2. ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ ๋กœ์ง ํ…Œ์ŠคํŠธ

  • ๋งคํ•‘๋œ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•จ

3. ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ฒ€์ฆ

  • ์ž˜๋ชป๋œ ์š”์ฒญ ๋ฐ ๋น„์ •์ƒ์  ์ž…๋ ฅ์— ๋Œ€ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
  • ์ •์˜๋œ ์—๋Ÿฌ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ํ™•์ธ


๐Ÿ”— Mock๊ณผ DispatcherServlet

  • MockMvc ๋Š” ์‹ค์ œ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋Š” ๊ตฌ๋™ํ•˜์ง€ ์•Š์ง€๋งŒ, DispatcherServlet ์˜ ํ•ต์‹ฌ ๋กœ์ง์„ Mock ํ™˜๊ฒฝ์—์„œ ๋™์ž‘์‹œํ‚ด
    • ServletContainer : Tomcat, Jetty ๋“ฑ, ์‹ค์ œ HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์„œ๋ธ”๋ฆฟ์„ ์‹คํ–‰ํ•˜๋Š” ํ™˜๊ฒฝ
    • MockMvc ๋Š” ์ด๋Ÿฌํ•œ ์‹ค์ œ HTTP ์š”์ฒญ ๋Œ€์‹ , MockHttpServletRequest ์™€ MockHttpServletResponse ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DispatcherServlet ๋‚ด๋ถ€ ๋กœ์ง ํ˜ธ์ถœ
  • DispatcherServlet ์€ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ ์—†์ด๋„ Spring MVC ์˜ ์š”์ฒญ-์‘๋‹ต ์ฒ˜๋ฆฌ ๊ณผ์ •์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๊ฐ€๋Šฅ


โžก๏ธ ๋™์ž‘ ๋ฐฉ์‹

1. Mock ์š”์ฒญ ์ƒ์„ฑ

  • MockMvc ๋Š” ์‹ค์ œ HTTP ์š”์ฒญ์ด ์•„๋‹Œ, MockHttpServletRequest ๋ฅผ ์ƒ์„ฑ

2. DispatcherServlet ํ˜ธ์ถœ

  • DispatcherServlet ์ธ์Šคํ„ด์Šค๋ฅผ Mock ํ™˜๊ฒฝ์—์„œ ์ดˆ๊ธฐํ™”
  • service() ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
    • ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ ์—†์ด DispatcherServlet ๋‚ด๋ถ€ ๋กœ์ง ์‹คํ–‰

3. ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘ ๋ฐ ์‹คํ–‰

  • DispatcherServlet ์€ ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘์„ ํ†ตํ•ด ์š”์ฒญ URL์— ๋งž๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฐพ๊ณ , ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ(ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ, ์•ก์…˜ ๋ฉ”์„œ๋“œ)๋ฅผ ์‹คํ–‰

4. Mock ์‘๋‹ต ๋ฐ˜ํ™˜

  • ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’์€ MockHttpServletResponse ์— ๋‹ด๊ฒจ ์‘๋‹ต ๊ฐ์ฒด๋กœ ์‚ฌ์šฉ

โœ’๏ธ MockMvc ์‚ฌ์šฉ

1. MockMvc ์ƒ์„ฑ

@SpringBootTest
@AutoConfigureMockMvc
public class MockControllerTest() {
	@Autowired
    private MockMvc mvc;
    
    @Test
    void t1() {
    	ResultActions resultActions = mvc.perform(...);
    }
}
  • AutoConfigureMockMvc ๋ฅผ ํ†ตํ•ด MockMvc ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •
  • ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” AllArgsConstructor , RequiredArgsConstructor ๋“ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, @Autowired ๋ฅผ ํ†ตํ•ด DI ๋กœ MockMvc ์ฃผ์ž…
  • perform() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด URL ์š”์ฒญ, ๊ฒฐ๊ณผ๋ฅผ ResultActions ๊ฐ์ฒด๋กœ ๋ฐ›์•„์˜ด

2. HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ

resultActions = mvc.perform(
	get("/api/v1/posts")
);

resultActions = mvc.perform(
	delete("/api/v1/posts/1")
);
  • HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒ
  • get(String url) : Get ์š”์ฒญ
  • post(String url) : Post ์š”์ฒญ
  • put(String url) : Put ์š”์ฒญ
  • delete(String url) : Delete ์š”์ฒญ
  • patch(String url) : Patch ์š”์ฒญ

3. andExpect()

mvc.perform(
	delete("/api/v1/posts/1")
)
.andExpect(status().isOk());
  • ์š”์ฒญ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
  • status() : ์‘๋‹ต ์ƒํƒœ ์ฝ”๋“œ ๊ฒ€์ฆ
  • header() : ์‘๋‹ต ํ—ค๋” ๊ฒ€์ฆ
  • handler() : ์‘๋‹ต ํ•ธ๋“ค๋Ÿฌ ๊ฒ€์ฆ
  • jsonPath() : JSON ์‘๋‹ต์˜ ํŠน์ • ํ•„๋“œ ๊ฒ€์ฆ

4. andDo()

mvc.perform(
	get("/api/v1/posts")
)
.andDo(print());
  • ์š”์ฒญ ๋ฐ ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€ ์ž‘์—… ์ง„ํ–‰
  • ๋””๋ฒ„๊น… ์šฉ๋„๋กœ ์ฃผ๋กœ ์‚ฌ์šฉ

5. header()

String apiKey = UUID.randomUUID().toString();

mvc.perform(
	post("/api/v1/posts")
    	.header("Authorization", "Bearer " + apiKey)
);
  • HTTP ํ—ค๋” ์ถ”๊ฐ€
  • ์ฟ ํ‚ค ํ™•์ธ ๋ฐ ๊ฒ€์ฆ ๋“ฑ์— ์ฃผ๋กœ ์‚ฌ์šฉ

6. content(), contentType()

mvc.perform(
	post("/api/v1/posts")
    	.content("""
        		{
                	"title": "์ œ๋ชฉ",
                    "content": "๋‚ด์šฉ"
                }
                """)
        .contentType(new MediaType(MediaType.APPLICATION_JSON, StandardCharsets.UTF_8))
);
  • content() : ์š”์ฒญ์— ๋ณธ๋ฌธ ์ถ”๊ฐ€
  • contentType() : ๋ณธ๋ฌธ ํŒŒ์ผ ํ˜•์‹ ๋ฐ ์ธ์ฝ”๋”ฉ ๋ฐฉ์‹ ์ง€์ •

์ฐธ๊ณ ) OpenAI. (2024).ChatGPT(4o)[Large language model].https://chatgpt.com/

profile
๊ธฐ๋ก

0๊ฐœ์˜ ๋Œ“๊ธ€