[Spring] Hamcrest , SliceTest

Updated:

Categories:

Tags: , , ,

๐Ÿ“Œ ๊ฐœ์ธ์ ์ธ ๊ณต๊ฐ„์œผ๋กœ ๊ณต๋ถ€๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ๋ณต์Šตํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ธ”๋กœ๊ทธ์ž…๋‹ˆ๋‹ค.
์ •ํ™•ํ•˜์ง€ ์•Š์€ ์ •๋ณด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ๋ฐ”๋ž๋‹ˆ๋‹ค :๐Ÿ˜ธ
[ํ‹€๋ฆฐ ๋‚ด์šฉ์€ ๋Œ“๊ธ€๋กœ ๋‚จ๊ฒจ์ฃผ์‹œ๋ฉด ๋ณต๋ฐ›์œผ์‹ค๊ฑฐ์—์š”]

Hamcrest

Hamcrest๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

  • Assertion์„ ์œ„ํ•œ ๋งค์ณ(Matcher)๊ฐ€ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฌธ์žฅ์œผ๋กœ ์ด์–ด์ง€๋ฏ€๋กœ ๊ฐ€๋…์„ฑ์ด ํ–ฅ์ƒ๋œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋‹ค.
  • ๋‹ค์–‘ํ•œ Matcher๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

๋ฌธ๋ฒ•

  1. AssertThat

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     import org.junit.jupiter.api.DisplayName;
     import org.junit.jupiter.api.Test;
        
     import static org.hamcrest.MatcherAssert.assertThat;
     import static org.hamcrest.Matchers.equalTo;
     import static org.hamcrest.Matchers.is;
        
     public class HelloHamcrestTest {
        
         @DisplayName("Hello Junit Test using hamcrest")
         @Test
         public void assertionTest1() {
             String expected = "Hello, JUnit";
             String actual = "Hello, JUnit";
        
             assertThat(actual, is(equalTo(expected)));  // (1)
         }
     }
        
    
    • assertThat(actual, is(equalTo(expected))); : โ€˜๊ฒฐ๊ณผ ๊ฐ’(actual)์ด ๊ธฐ๋Œ€ ๊ฐ’(expected)๊ณผ ๊ฐ™๋‹ค๋Š” ๊ฒƒ์„ ๊ฒ€์ฆ(Assertion)โ€™
    • assertThat() ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ
      • ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ : ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์˜ ์‹ค์ œ ๊ฒฐ๊ณผ ๊ฐ’
      • ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ : ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์ด๋Ÿฐ ๊ฐ’์ผ ๊ฑฐ๋ผ๊ณ  ๊ธฐ๋Œ€(์˜ˆ์ƒ)ํ•˜๋Š” ๊ฐ’
  2. Not Null ํ…Œ์ŠคํŠธ : Hamcrest์˜ is(), notNullValue() ๋งค์ณ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉ





์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ(Slice Test)

  • ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐ ๊ณ„์ธต์— ๊ตฌํ˜„ํ•ด ๋†“์€ ๊ธฐ๋Šฅ๋“ค์ด ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํŠน์ • ๊ณ„์ธต๋งŒ ์ž˜๋ผ์„œ(Slice) ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์„ ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ๊ณ„์ธต(์˜ˆ: ์›น ๊ณ„์ธต, ์„œ๋น„์Šค ๊ณ„์ธต ๋“ฑ)๋งŒ์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•.
  • ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ด๋‹ค.



API ๊ณ„์ธต ํ…Œ์ŠคํŠธ

๊ธฐ๋ณธ ๊ตฌ์กฐ(with @SpringBootTest , @AutoConfigureMockMvc)

  1. @SpringBootTest
    • @SpringBootTest ์• ๋„ˆํ…Œ์ด์…˜์€ Spring Boot ๊ธฐ๋ฐ˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•œ Application Context๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    • ์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ์ปจํ…์ŠคํŠธ๋ฅผ ๋กœ๋“œํ•˜์—ฌ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์คŒ (๋ชจ๋“  Bean์„ ๋กœ๋“œํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌด๊ฑฐ์›€)
    • ๊ฒฐ๊ตญ ํ•ด๋‹น ์• ๋„ˆํ…Œ์ด์…˜์ด ๋ถ™์œผ๋ฉด ์Šคํ”„๋ง๋ถ€ํŠธ์˜ ๋„์›€์„ ๋ฐ›๊ฒ ์Šต๋‹ˆ๋‹ค ์ฆ‰ ,์Šคํ”„๋ง์˜ ์ฝ”์–ด ๊ธฐ๋Šฅ์„ ์“ฐ๊ฒ ๋‹ค๋Š” ๋œป

      โ†’ ๊ทธ๋ž˜์„œ @Autowired ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž„.

      โ†’ Autowired ๋Š” ํ•„๋“œ ์ฃผ์ž… : ํ•„๋“œ ์ฃผ์ž…์€ ์‚ฌ์šฉํ•˜์ง€ ๋ง๋ผ๊ณ  ํ•˜์ง€๋งŒ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค

  2. @AutoConfigureMockMvc
    • ํ™˜๊ฒฝ์„ค์ •์„ ํ•ด์ฃผ๋Š”๊ฒƒ.( Controller ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ž๋™ ๊ตฌ์„ฑ ์ž‘์—…์„ ํ•ด์ค€๋‹ค.)
    • MockMVC๋ฅผ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค
  3. API ๊ณ„์ธต๊ณผ ์„œ๋ธ”๋ฆฟ
    • ์Šคํ”„๋ง๋ถ€ํŠธ๋Š” ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋ผ๋Š” ํ™˜๊ฒฝ์—†์ด๋Š” ์‹คํ–‰ํ•  ์ˆ˜์—†๋‹ค, ์™œ๋ƒ๋ฉด ์„œ๋ธ”๋ฆฟ์ด ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—
    • ์„œ๋ธ”๋ฆฟ. ํ•˜๋‚˜์˜ ์„œ๋ธ”๋ฆฟ ๊ฐ์ฒด(์ž๋ฐ”์—์„œ ์›น ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ์ฒด)๊ฐ€ ์‹คํ–‰
    • ์„œ๋ธ”๋ฆฟ์„ ๋‹ด๋‹นํ•˜๋Š”๊ฒŒ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ (= tomcat)

    • ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ ์•ž์— ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์žˆ์Œ. was๋ผ๊ณ ๋„ํ•˜๊ณ  tomcat์ด๋ผ๊ณ ๋„ ํ•จ.
    • ์›น์˜ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๋Š” ๊ฒƒ์ด ์„œ๋ธ”๋ฆฟ์ด๊ณ . ์ด๊ฑธ ๋‹ด๋‹นํ•˜๋Š”๊ฒŒ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ (์„œ๋ธ”๋ฆฟ์ปจํ…Œ์ด๋„ˆ์˜ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ : ํ†ฐ์บฃ)
    • ๊ฒฐ๊ตญ API๊ณ„์ธต์—์„œ ์„œ๋ธ”๋ฆฟ์€ ํ•„์ˆ˜ , ์–˜๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ฃผ์ฒด๊ฐ€ ํ•„์š”ํ•จ
    • ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ธฐ์œ„ํ•ด์„œ๋Š” ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํ•„์ˆ˜๋กœ ํ•„์š”ํ•จ โ†’? ์™œ๋ƒ๋ฉด ๊ฑฐ๊ธฐ๊นŒ์ง€ ๋ณด๋‚ด์ฃผ๋Š” ์• ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์—.
    • MockMVC ๋Š” ๊ฐ€์งœ๋กœ MVC์˜ ๊ตฌ์กฐ๋ฅผ ๋‚˜ํƒ€๋ƒ„, ์‹ค์ œ๋กœ ์„œ๋ธ”๋ฆฟ ์—†์ด API ๊ณ„์ธต์—๊ฒŒ ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ๋ณด๋‚ด๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
    • ํ…Œ์ŠคํŠธ ํ• ๋•Œ๋งˆ๋‹ค ํ†ฐ์บฃ์„ ์‹คํ–‰ํ•˜๋ฉด ๋งค๋ฒˆ ๋งŽ์€ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๋‹ˆ MockMVC๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ฃผ์ž…์ด ํ•„์š”ํ•จ.
    • ๊ทธ๋ž˜์„œ ํ™˜๊ฒฝ์„ค์ •์ด ํ•„์š”ํ•ด์„œ AutoConfigureMockMVC์™€ SpringBootTest ์• ๋„ˆํ…Œ์ด์…˜์ด ํ•„์š”ํ•˜๊ฒŒ ๋œ๋‹ค.

      **
    • ๐Ÿ”Ž ์ฐธ๊ณ 

      ์„œ๋ธ”๋ฆฟ(Servlet)

      • ์ž๋ฐ”์—์„œ ์›น ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ์ฒด
      • ํด๋ผ์ด์–ธํŠธ์˜ HTTP ์š”์ฒญ์„ ๋ฐ›์•„์„œ ์ ์ ˆํ•œ ์‘๋‹ต์„ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

      ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ(Servlet Container)

      • ์„œ๋ธ”๋ฆฟ์„ ๊ด€๋ฆฌํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ํ™˜๊ฒฝ
      • ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ Tomcat
      • ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋Š” ์›น ์„œ๋ฒ„์™€ ์„œ๋ธ”๋ฆฟ ์‚ฌ์ด์˜ ํ†ต์‹ ์„ ๊ด€๋ฆฌํ•˜๋ฉฐ, ์„œ๋ธ”๋ฆฟ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌ

      DispatcherServlet

      • ์Šคํ”„๋ง MVC์˜ ํ•ต์‹ฌ ์„œ๋ธ”๋ฆฟ์œผ๋กœ, ๋ชจ๋“  HTTP ์š”์ฒญ์„ ๋ฐ›์•„ ์ ์ ˆํ•œ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์š”์ฒญ์„ ์ „๋‹ฌํ•˜๊ณ , ์‘๋‹ต์„ ์ƒ์„ฑ

      MockMVC

      • ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ ๋„ ์Šคํ”„๋ง MVC์˜ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ
      • MockMVC๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‹ค์ œ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ ๋„ ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.

  4. MokcMvc
    • MockMvc๋Š” Tomcat ๊ฐ™์€ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  Spring ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ Controller๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ์™„๋ฒฝํ•œ ํ™˜๊ฒฝ์„ ์ง€์›ํ•ด ์ฃผ๋Š” ์ผ์ข…์˜ Spring MVC ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ
    • MockMvc๋กœ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ Controller์˜ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์— ์š”์ฒญ์„ ์ „์†กํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ perform() ๋ฉ”์„œ๋“œ๋ฅผ ๋จผ์ € ํ˜ธ์ถœ ํ•ด์•ผ ํ•จ. (์•„๋ž˜ postMember( ) ์˜ˆ์ œ์˜ then ๋ถ€๋ถ„ ์ฐธ๊ณ )
    • MockMvcRequestBuilders ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด์„œ ๋นŒ๋” ํŒจํ„ด์„ ํ†ตํ•ด request ์ •๋ณด๋ฅผ ์ฑ„์›Œ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.
    • MockMvc์˜ perform() ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฆฌํ„ดํ•˜๋Š” ResultActions ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ request์— ๋Œ€ํ•œ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ


MemberCotroller ํ…Œ์ŠคํŠธ โ†’ HTTP Post request์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ

  1. Gson ์ถ”๊ฐ€

  2. MemberDto ํด๋ž˜์Šค

    ๊ธฐ์กด Dtoํด๋ž˜์Šค๋Š” ๋‹ค ๋‚˜๋ˆ ์ ธ ์žˆ์—ˆ๋Š”๋ฐ ํ•˜๋‚˜์˜ dtoํด๋ž˜์Šค์— post/patch/response๋ฅผ ์ด๋„ˆํด๋ž˜์Šค๋กœ ํ†ตํ•ฉํ•ด์„œ ๋งŒ๋“ค์–ด ๋†“์Œ

  3. MemberController์˜ postMember() ํ…Œ์ŠคํŠธ

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    package com.springboot.member.controller;
    //...
    ...import com.google.gson.Gson;
    //...
            
    @SpringBootTest
    @AutoConfigureMockMvc
    class MemberControllerTest {
        @Autowired
        private MockMvc mockMvc;
        @Autowired
        private Gson gson;
            
        @Test
        void postMemberTest() throws Exception {
             //(1) given
            MemberDto.Post post = new MemberDto.Post
                    ("jerry@gmail.com", "๋ฐ•์ œ๋ฆฌ", "010-1111-1111");
            String content = gson.toJson(post);
                    
            //(2) when
            //์‹ค์ œ api ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰
            ResultActions actions = mockMvc.perform(
            //(3)  
        	        post("/v11/members")
                        .accept(MediaType.APPLICATION_JSON) //์‘๋‹ต์€ JSON
                        .contentType(MediaType.APPLICATION_JSON) 
                        .content(content)
                );
                //then
            actions.andExpect(status().isCreated())
                    .andExpect(header().string("Location",is(startsWith("/v11/members/"))));
            }
        }
    

    1๏ธโƒฃ Given

    • Postman์„ ์‚ฌ์šฉํ•  ๋•Œ request body์— ํฌํ•จ์‹œํ‚ค๋Š” ์š”์ฒญ ๋ฐ์ดํ„ฐ์™€ ๋™์ผํ•œ ์—ญํ• 

    • Postman ์‚ฌ์šฉ์‹œ JSON ํฌ๋งท์œผ๋กœ ๋ฐ”๋””์— ํฌํ•จ์‹œ์ผฐ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ Gson์ด๋ผ๋Š” JSON ๋ณ€ํ™˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด MemberDto.Post ๊ฐ์ฒด๋ฅผ JSONํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜

    2๏ธโƒฃ When

    • perform()๋ฉ”์„œ๋“œ ํ˜ธ์ถœ โ†’ MockMvc๋กœ Controller์˜ ํ•ธ๋“ค๋Ÿฌ๋ฉ”์„œ๋“œ์— ์š”์ฒญ์„ ์ „์†กํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ•„์š”ํ•จ, perform ๋‚ด์—๋Š” ํ˜ธ์ถœ์„ ์œ„ํ•œ ์„ธ๋ถ€์ ์ธ ์ •๋ณด๋“ค์ด ํฌํ•จ๋œ๋‹ค.
    • (3)๋ถ€ํ„ฐ๋Š” HTTP request์— ๋Œ€ํ•œ ์ •๋ณด์ด๋ฉฐ, MockMvcRequestBuilders ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด์„œ ๋นŒ๋” ํŒจํ„ด์„ ํ†ตํ•ด request ์ •๋ณด๋ฅผ ์ฑ„์›Œ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.
    • post() ๋ฅผ ํ†ตํ•ด HTTP POST METHOD์™€ request URL์„ ์„ค์ •
    • accept() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ ์ชฝ์—์„œ ๋ฆฌํ„ด ๋ฐ›์„ ์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ JSON ํƒ€์ž…์„ ์„ค์ • (๋ฐ์ดํ„ฐ๋Š” JSON ํƒ€์ž…์œผ๋กœ ๋ฐ›์„๊ฑฐ์•ผ)
    • contentType() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„ ์ชฝ์—์„œ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•œ Content Type์œผ๋กœ JSON ํƒ€์ž…์„ ์„ค์ • (์šฐ๋ฆฌ๊ฐ€ ๋ณด๋‚ด๋Š” ๋ฐ์ดํ„ฐ๋Š” JSON ํƒ€์ž…์œผ๋กœ ๋ณด๋‚ผ๊ฑฐ์•ผ)
    • content() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด request body ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •

    3๏ธโƒฃ Then

    • MockMvc์˜ perform() ๋ฉ”์„œ๋“œ๋Š” ResultActions ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•˜๋Š”๋ฐ, ์ด ResultActions ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ ์šฐ๋ฆฌ๊ฐ€ ์ „์†กํ•œ request์— ๋Œ€ํ•œ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰
    • [ResultActions ์ธํ„ฐํŽ˜์ด์Šค โ†’ andExpect๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฒ€์ฆ]

      • andExpect() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž…๋ ฅํ•œ ๋งค์ฒ˜(Matcher)๋กœ ์˜ˆ์ƒ๋˜๋Š” ๊ธฐ๋Œ€ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆ, ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” throw๋กœ ์˜ˆ์™ธ๋„ ํ•จ๊ป˜ ๋˜์ ธ์•ผ ํ•œ๋‹ค!
    • status().isCreated()๋ฅผ ํ†ตํ•ด response status๋ฅผ ๋งค์น˜
    • (header().string(โ€œLocationโ€,is(startsWith(โ€œ/v11/members/โ€)) โ‡’ HTTP header์— ์ถ”๊ฐ€๋œ Location์˜ ๋ฌธ์ž์—ด ๊ฐ’์ด โ€œ/v11/members/โ€๋กœ ์‹œ์ž‘ํ•˜๋Š”์ง€ ๊ฒ€์ฆ
  4. ์‹ค์ œ ๊ฒฐ๊ณผ

    • Body์— status ๊ฐ’ ํ™•์ธ ํ•ด์•ผ ํ•จ. Postman์‚ฌ์šฉํ•  ๋•Œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ @Valid ๊ฒ€์ฆ์ด ๋“ค์–ด๊ฐ€์„œ ์˜ค๋ฅ˜๊ฐ€ ๋œจ๋Š” ๊ฒƒ.

    • ํ…Œ์ŠคํŠธ ์‹คํ–‰์‹œ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ์— ๋Œ€ํ•œ ๋‚ด์šฉ์ด ์—†์Œ.

    • cotroller์˜ ํ…Œ์ŠคํŠธ๋งŒ ํ•ด์•ผํ•˜๋Š”๋ฐ Hibernate์˜ ๋„์›€์„ ๋ฐ›๊ณ  ์ž‡์Œ.

MemberCotroller ํ…Œ์ŠคํŠธ โ†’ HTTP get request์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    @Test
    void getMemberTest() throws Exception {
    	     
     //Given , ์•ž์„œ ์ž‘์„ฑํ–ˆ๋˜ postMember()๋ฅผ ์ด์šฉํ•œ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์‹œ์ž‘   
        MemberDto.Post post = new MemberDto.Post("jerry@gmail.com","wpfl","010-5555-5555");
        String content = gson.toJson(post);
        ResultActions postActions=  mockMvc.perform(post("/v11/members")
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON)
                .content(content));
    
        postActions.andExpect(status().isCreated())
                .andExpect(header().string("Location", is(startsWith("/v11/members"))));
    
            
        //"v11/member/1"
        String location = postActions.andReturn().getResponse().getHeader("Location");
            
        //when then
        ResultActions response =  mockMvc.perform(get(location).accept(MediaType.APPLICATION_JSON));
        response.andExpect(status().isOk())
                .andExpect(jsonPath("$.data.email").value(post.getEmail()))
                .andExpect(jsonPath("$.data.name").value(post.getName()))
                .andExpect(jsonPath("$.data.phone").value(post.getPhone()));
    
        //get ์š”์ฒญ์„ ๋ณด๋‚ด์•ผํ•œ๋‹ค db์— ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š”์ง€
    }}

1๏ธโƒฃ Given

  • postMember()์™€ ๋™์ผํ•œ ์ฝ”๋“œ, ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„ ์ธก์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋จผ์ € ์ €์žฅ
  • postActions.andReturn().getResponse().getHeader("Location")๋กœ ์ ‘๊ทผํ•ด์„œ Location header์˜ ๊ฐ’์„ ์–ป์–ด ์˜ฌ ์ˆ˜ ์žˆ์Œ

2๏ธโƒฃ When

  • location header ๊ฐ’์„ get์˜ URI๋กœ ์ „๋‹ฌ
  • Location header์—์„œ ์–ป๊ฒŒ ๋˜๋Š” ๊ฐ’์ด given์—์„œ ๋“ฑ๋กํ•œ ํšŒ์›์ •๋ณด์˜ ์œ„์น˜๋ฅผ ์˜๋ฏธ (โ€/v11/members/1โ€)

3๏ธโƒฃ Then

  • ๊ธฐ๋Œ€ํ•˜๋Š” Http statusrk 200 OK์ธ์ง€ ๊ฒ€์ฆ
  • jsonPath()๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด response body(JSON ํ˜•์‹)์˜ ๊ฐ ํ”„๋กœํผํ‹ฐ๋กœ ์‘๋‹ต๋ฐ›๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ response body๋กœ ์ „์†กํ•œ ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ๊ฒ€์ฆ

โœ”๏ธ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

  • ์ „๋ถ€ ํ…Œ์ŠคํŠธ ํ•˜๋ฉด get๋งŒ ํ†ต๊ณผํ•จ ์™œ๋ƒ๋ฉด postํ•  Member๋Š” ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋ฉค๋ฒ„์ด๊ธฐ ๋•Œ๋ฌธ

  • @Transactional ์ถ”๊ฐ€์‹œ ๋ชจ๋‘ ํ†ต๊ณผํ•จ. โ†’ ๋ช‡๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ.

๐Ÿค” ๊ฐ๊ฐ์˜ Test ์ผ€์ด์Šค๋Š” ๋…๋ฆฝ์ ์ธ ์—ญํ• ์„ ํ•ด์•ผํ•˜๋Š”๋ฐ ์›์น™์„ ๋ฒ—์–ด๋‚˜๊ณ  ์žˆ์Œ

  • ์Šฌ๋ผ์ด์Šคํ…Œ์ŠคํŠธ๋Š” ๋…๋ฆฝ์ ์œผ ์ปจํŠธ๋กค๋Ÿฌ๋งŒ์„ ํ…Œ์ŠคํŠธํ•ด์•ผ๋˜๋Š”๋ฐ DB๋„ ์ ‘๊ทผํ•˜๊ณ  ์žˆ๊ณ , DB์— ์ ‘๊ทผํ•˜๋ ค๋ฉด ์„œ๋น„์Šค๋„ ๊ฑฐ์ณ์•ผ ํ•จ.
  • controller service repository๊นŒ์ง€ ๋ชจ๋‘ ๊ตฌํ˜„๋˜์–ด์•ผ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ (Timely ๋ถˆ๊ฐ€)
  • ์‚ฌ์‹ค์ƒ ์Šฌ๋ผ์ด์Šคํ…Œ์ŠคํŠธ๋ผ๊ธฐ ๋ณด๋‹ค ์„œ๋ธ”๋ฆฟ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ†ตํ•ฉํ…Œ์ŠคํŠธ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Œ

โœ”๏ธ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋“ค์€ Mock(๊ฐ€์งœ) ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๊ณ„์ธต ๊ฐ„์˜ ์—ฐ๊ฒฐ์„ ๋Š์–ด์คŒ์œผ๋กœ์จ ํ•ด๊ฒฐ์ด ๊ฐ€๋Šฅ โ†’ ๋‹ค์Œ์— ๋ฐฐ์šธ ๋‚ด์šฉ!

โœ”๏ธ @AutoConfigureMockMvc ๋ง๊ณ  @webMvcTest ๋ผ๋Š” ๊ฒƒ๋„ ์žˆ๋Š”๋ฐ DI๋“ฑ ์„ค์ •ํ•ด์ค˜์•ผ ํ• ๊ฒŒ ๋งŽ์•„์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œโ†’ ๋ฌธ์„œํ™”ํ•  ๋•Œ ๋‚˜์ค‘์— ์‚ฌ์šฉํ•  ๊ฒƒ.

๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๊ณ„์ธต ํ…Œ์ŠคํŠธ

๐Ÿ”‘ ๊ผญ ์ง€ํ‚ค๊ธฐ : ๊ฐ๊ฐ์˜ TestCase๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋‚˜์„œ๋Š” ๋กค๋ฐฑํ•ด์•ผ ํ•จ!

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ผ๊ด„์ ์œผ๋กœ ์‹คํ–‰์‹œํ‚ค๋”๋ผ๋„ ๊ฐ๊ฐ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ๋…๋ฆฝ์„ฑ์ด ๋ณด์žฅ๋˜์–ด์•ผ ํ•œ๋‹ค.

์ฆ‰, DB์˜ ์ƒํƒœ๋ฅผ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰ ์ด์ „์œผ๋กœ ๋˜๋Œ๋ ค์„œ ๊นจ๋—ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ํ•„์š”

์˜ˆ์ œ - MemberRepository ํ…Œ์ŠคํŠธ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.springboot.member.repository;

import com.springboot.member.entity.Member;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

@DataJpaTest //(1)
class MemberRepositoryTest {
    @Autowired
    private MemberRepository memberRepository;

    @Test
    public void saveMemberTest(){
        //given
        Member member = new Member();
        //์ดˆ๊ธฐํ™” ํ•  ๋•Œ Id = 0; ์ด ๋ฉค๋ฒ„์˜ id๋Š” ํ•ญ์ƒ 0์ž„
        //test ํ™˜๊ฒฝ์—์„œ๋Š” ๊ธฐ๋ณธํ‚ค๋Š” ๊ฒ€์ฆํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Œ.

        member.setEmail("jerry@gmail.comn=");
        member.setName("์ œ๋ฆฌ");
        member.setPhone("010-0430-0430");

        //When
        Member savedMember = memberRepository.save(member);

        //then
        assertNotNull(savedMember);
        assertEquals(member.getEmail(), savedMember.getEmail());
        assertEquals(member.getName(),savedMember.getName());
        assertEquals(member.getPhone(),savedMember.getPhone());
    }

    @Test
    public void findByEmailTest(){
        //given
        Member member  = new Member();
        member.setEmail("jerry@gmail.comn=");
        member.setName("์ œ๋ฆฌ");
        member.setPhone("010-0430-0430");

        //when
        memberRepository.save(member);
        Optional<Member> findMember = memberRepository.findByEmail(member.getEmail());
        //repository๋Š” ๋ชจ๋‘ Optional ํƒ€์ž…์ด์—ˆ์Œ.

        //then
        assertTrue(findMember.isPresent());  //null์ด ์•„๋‹Œ์ง€๋ฅผ ๊ฒ€์ฆ
        assertEquals(findMember.get().getEmail(), member.getEmail());
        assertEquals(findMember.get().getPhone(),member.getPhone());
        assertEquals(findMember.get().getMemberStatus(), member.getMemberStatus());
    }
}
  1. (1) @DataJpaTest

    ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๊ณ„์ธต์— ํ•„์š”ํ•œ ์ž๋™ ๊ตฌ์„ฑ์„ ํ™œ์„ฑํ™” ํ•˜๋Š” ๊ธฐ๋Šฅ

    • @Transactional ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ํ•˜๋‚˜์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰์ด ์ข…๋ฃŒ๋˜๋Š” ์‹œ์ ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋Š” rollback ์ฒ˜๋ฆฌ๋œ๋‹ค. โ†’ ์ž๋™์œผ๋กœ ๋กค๋ฐฑ๋จ.
  2. ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ํด๋ž˜์Šค์ธ MemberRepository๋ฅผ DI ๋ฐ›๋Š”๋‹ค.
  3. given : ํ…Œ์ŠคํŠธ ํ•  ํšŒ์› ์ •๋ณด๋ฅผ ์ค€๋น„ ํ›„
  4. when : repository์— ์ €์žฅ
  5. then : ํšŒ์› ์ •๋ณด๊ฐ€ ์ž˜ ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆ(Assertion)

Spring ์นดํ…Œ๊ณ ๋ฆฌ ๋‚ด ๋‹ค๋ฅธ ๊ธ€ ๋ณด๋Ÿฌ๊ฐ€๊ธฐ

Leave a comment