๐Ÿ’ Spring/Spring REST API

3) [test] ์ž…๋ ฅ๊ฐ’ ์ œํ•œํ•˜๊ธฐ (๋ฌด์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•) - ์Šคํ”„๋ง REST API

2020. 9. 9. 15:30
๋ชฉ์ฐจ
  1. ํ…Œ์ŠคํŠธ ํ•  ๊ฒƒ
  2. ์ž…๋ ฅ๊ฐ’ ์ œํ•œ
  3. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋กœ ์ „ํ™˜
  4. EventController.class
  5. EventDTO.class
  6. EventControllerTests.class
๋ฐ˜์‘ํ˜•

ํ…Œ์ŠคํŠธ ํ•  ๊ฒƒ

  • ์ž…๋ ฅ๊ฐ’์œผ๋กœ ๋ˆ„๊ฐ€ id๋‚˜ eventStatus, offline, free ์ด๋Ÿฐ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๊ฐ™์ด ์ฃผ๋ฉด?
    • Bad_Request ๋กœ ์‘๋‹ต vs ๋ฐ›๊ธฐ๋กœ ํ•œ ๊ฐ’ ์ด์™ธ๋Š” ๋ฌด์‹œ! (์ด๋ฒˆ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•)

์ž…๋ ฅ๊ฐ’ ์ œํ•œ

  • id ๋˜๋Š” ์ž…๋ ฅ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋กœ ๊ณ„์‚ฐํ•ด์•ผ ํ•˜๋Š” ๊ฐ’๋“ค์€ ์ž…๋ ฅ ๋ฐ›์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.
  • EventDTO ์ ์šฉ

DTO -> ๋„๋ฉ”์ธ ๊ฐ์ฒด๋กœ ๊ฐ’ ๋ณต์‚ฌ

  • ModelMapper

์˜์กด์„ฑ ์ถ”๊ฐ€

<dependency> 
<groupId>org.modelmapper</groupId> 
<artifactId>modelmapper</artifactId>
<version>2.3.1</version> 
</dependency> 

๋นˆ์œผ๋กœ ๋“ฑ๋ก @Configuration ์•„๋ž˜์—

@Bean
    public ModelMapper modelMapper(){
        return new ModelMapper();
    }

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋กœ ์ „ํ™˜

  • @WebMvcTest ๋นผ๊ณ  ๋‹ค์Œ ์• ๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€
    • @SpringBootTest
    • @AutoConfigureMockMvc
  • Repository @MockBean ์ฝ”๋“œ ์ œ๊ฑฐ

EventController.class

@Controller
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)

public class EventController {

    private final EventRepository eventRepository;
    private final ModelMapper modelMapper;

    //์ƒ์„ฑ์ž๊ฐ€ ํ•˜๋‚˜๋งŒ์žˆ๊ณ , ๋ฐ›์•„์˜ฌ ํƒ€์ž…์ด ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜์–ด์žˆ์œผ๋ฉด autowired ์ƒ๋žต ๊ฐ€๋Šฅ
    public EventController(EventRepository eventRepository, ModelMapper modelMapper) {
        this.eventRepository = eventRepository;
        this.modelMapper = modelMapper;
    }

    @PostMapping
    public ResponseEntity createEvent(@RequestBody EventDto eventDto) {
        /*Event event = Event.builder()
                .name(eventDto.getName())
                ...
                .build(); ๋ฅผ ์†์‰ฝ๊ฒŒ ๋งคํ•‘ํ•ด์ฃผ๋Š” ModelMapper๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.*/

        Event event = modelMapper.map(eventDto, Event.class);

        Event newEvent = this.eventRepository.save(event);
        //link๋ฅผ ์ƒ์„ฑํ• ๋•,
        //HATEOAS๊ฐ€ ์ œ๊ณตํ•˜๋Š” linkTo(), methodOn()์„ ์‚ฌ์šฉ
        URI createUri = linkTo(EventController.class).slash(newEvent.getId()).toUri();
        return ResponseEntity.created(createUri).body(event); //201์‘๋‹ต์„ Uri์— ๋‹ด์•„์„œ ๋ฆฌํ„ด์‹œํ‚จ๋‹ค.
    }
}

EventDTO.class

package me.iseunghan.demoinflearnrestapi.events;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data @Builder @AllArgsConstructor @NoArgsConstructor
public class EventDto {

    private String description;
    private LocalDateTime beginEnrollmentDateTime;
    private LocalDateTime closeEnrollmentDateTime;
    private LocalDateTime beginEventDateTime;
    private LocalDateTime endEventDateTime;
    private String location; // (optional) ์ด๊ฒŒ ์—†์œผ๋ฉด ์˜จ๋ผ์ธ ๋ชจ์ž„
    private int basePrice; // (optional)
    private int maxPrice; // (optional)
    private int limitOfEnrollment;
}

EventControllerTests.class

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class EventControllerTests {

    @Autowired
    MockMvc mockMvc;

    @Autowired
    ObjectMapper objectMapper;


    @Test
    public void createEvent() throws Exception {
        Event event = Event.builder()
                .id(100)
                .name("Spring")
                .description("REST API Development with Spring")
                .beginEnrollmentDateTime(LocalDateTime.of(2020, 9, 7, 2, 45))
                .closeEnrollmentDateTime(LocalDateTime.of(2020, 9, 8, 2, 45))
                .beginEventDateTime(LocalDateTime.of(2020, 9, 9, 2, 45))
                .endEventDateTime(LocalDateTime.of(2020, 9, 10, 2, 45))
                .basePrice(100)
                .maxPrice(200)
                .limitOfEnrollment(100)
                .location("Daejoen")
                .free(true)
                .offline(false)
                .eventStatus(EventStatus.PUBLISHED)
                .build();
        //Mock๊ฐ์ฒด๋กœ ๋ฐ›์•˜๊ธฐ ๋•Œ๋ฌธ์— save๋„ ์•ˆ๋ ๊ฒƒ์ด๊ณ , NullpointerException์ด ๋ฐœ์ƒํ• ๊ฒƒ์ด๋‹ค.
        //๊ทธ๋ฆฌํ•˜์—ฌ Mockito.when(eventRepository.save(event)).thenReturn(event);
        //eventRepository.save๊ฐ€ ํ˜ธ์ถœ์ด ๋˜๋ฉด -> ๊ทธ๋‹ค์Œ event๋ฅผ ๋ฆฌํ„ดํ•˜๋ผ.
        //Mockito.when(eventRepository.save(event)).thenReturn(event);

        mockMvc.perform(post("/api/events/")
                .contentType(MediaType.APPLICATION_JSON)//๋ณธ๋ฌธ ์š”์ฒญ์— json์„ ๋‹ด์•„์„œ ๋ณด๋‚ด๊ณ  ์žˆ๋‹ค๊ณ  ์•Œ๋ ค์คŒ.
                .accept(MediaTypes.HAL_JSON)//HAL_JSON์œผ๋กœ ๋ฐ›๋Š”๋‹ค.
                .content(objectMapper.writeValueAsString(event)))//์š”์ฒญ ๋ณธ๋ฌธ์— ๋„ฃ์–ด์ค€๋‹ค. objectMapper๋กœ event๋ฅผ json์œผ๋กœ ๋ณ€ํ™˜ํ›„
                .andDo(print())//์–ด๋–ค ์‘๋‹ต๊ณผ ์š”์ฒญ์„ ๋ฐ›์•˜๋Š”์ง€ ํ™•์ธ๊ฐ€๋Šฅ.
                .andExpect(status().isCreated())//201์š”์ฒญ์ด ๋“ค์–ด์™”๋Š”์ง€?
                .andExpect(jsonPath("id").exists()) //json์— id๊ฐ€ ์žˆ๋Š”์ง€?
                .andExpect(header().exists(HttpHeaders.LOCATION))//ํ—ค๋”์— Location์ด ์žˆ๋Š”์ง€
                .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_JSON_VALUE))//content-type์— "application/hal+json"๊ฐ€ ๋‚˜์˜ค๋Š”์ง€?
                .andExpect(jsonPath("id").value(Matchers.not(100)))
                .andExpect(jsonPath("free").value(Matchers.not(true)))
                .andExpect(jsonPath("eventStatus").value(EventStatus.PUBLISHED.name()))
                ;

    }

}

์ด๋ฒˆ ํ…Œ์ŠคํŠธ๋Š” @WebMvcTest ๊ฐ€ ์•„๋‹Œ, @SpringBootTest ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ง„์งœ ์Šคํ”„๋ง์„ ๋„์›Œ์„œ @SpringBootApplication ํŒจํ‚ค์ง€์—
๋ชจ๋“  ๋นˆ๋“ค์„ ๋“ฑ๋ก์‹œ์ผœ์ค€๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ, Mockito๊ฐ€ ์•„๋‹Œ! Controller์—์„œ ์ง์ ‘ repository.save๋ฅผ ์‹œ์ผœ์ฃผ๋ฉด ๋œ๋‹ค.

๋ฐ˜์‘ํ˜•
์ €์ž‘์žํ‘œ์‹œ (์ƒˆ์ฐฝ์—ด๋ฆผ)
  1. ํ…Œ์ŠคํŠธ ํ•  ๊ฒƒ
  2. ์ž…๋ ฅ๊ฐ’ ์ œํ•œ
  3. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋กœ ์ „ํ™˜
  4. EventController.class
  5. EventDTO.class
  6. EventControllerTests.class
'๐Ÿ’ Spring/Spring REST API' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • 5) [test] ์ž…๋ ฅ๊ฐ’ ์ œํ•œํ•˜๊ธฐ (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์œผ๋กœ ๊ฒ€์‚ฌ) - ์Šคํ”„๋ง REST API
  • 4) [test] ์ž…๋ ฅ๊ฐ’ ์ œํ•œํ•˜๊ธฐ (Bad_Request ๋ฐœ์ƒ) - ์Šคํ”„๋ง REST API
  • 2) [test] JSON ์‘๋‹ต์œผ๋กœ 201์ด ๋‚˜์˜ค๋Š”์ง€ ํ™•์ธ - ์Šคํ”„๋ง REST API
  • 1) Spring-Boot ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ - ์Šคํ”„๋ง REST API
iseunghan
iseunghan
๊พธ์ค€ํ•˜๊ฒŒ ์—ด์‹ฌํžˆ..
iseunghan
iseunghan

๊ณต์ง€์‚ฌํ•ญ

  • ์–ด์ œ๋ณด๋‹ค ๋‚˜์€ ์˜ค๋Š˜์ด ๋˜๊ธฐ ์œ„ํ•ด ๐Ÿ”ฅ
  • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (260)
    • ๐Ÿ’ Spring (14)
      • ๊ฐœ๋… ๋ฐ ์ดํ•ด (2)
      • Spring ํ•ต์‹ฌ ๊ธฐ์ˆ  (24)
      • Spring REST API (8)
      • Spring MVC, DB ์ ‘๊ทผ ๊ธฐ์ˆ  (7)
      • Spring Security (23)
      • Spring in Action (1)
    • ๐ŸŒป JAVA (84)
      • ์ž๋ฐ” ORM ํ‘œ์ค€ JPA ํ”„๋กœ๊ทธ๋ž˜๋ฐ (20)
      • ์•Œ๊ณ ๋ฆฌ์ฆ˜, ์ž๋ฃŒ๊ตฌ์กฐ (13)
      • ๋””์ž์ธ ํŒจํ„ด (7)
      • ์ •๋ฆฌ์ •๋ฆฌ์ •๋ฆฌ (43)
      • JUnit (1)
    • ๐Ÿ”– Snippets (3)
      • Javascript (3)
    • โš™๏ธ Devops (22)
      • โ› Git (11)
      • ๐Ÿณ Docker (6)
      • ๐Ÿง Linux (3)
      • ๐ŸŒˆ Jenkins (1)
      • ๐Ÿ“ฌ Kafka (1)
    • ๐Ÿ’ฌ ETC.. (4)
      • ๐Ÿ’ป macOS (2)
    • ๐ŸŒง๏ธ ORM (2)
      • JPA (2)
    • ๐Ÿ Python (1)
    • ๐Ÿ“š Databases (15)
      • ์˜ค๋ผํด๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐœ๋ก ๊ณผ ์‹ค์Šต(2ํŒ) (3)
      • RealMySQL 8.0 (8)
    • ๐Ÿ”ฅ Computer Science (5)
      • ๐Ÿ“ก ๋„คํŠธ์›Œํฌ (5)
    • ๐Ÿท๏ธ ํ˜‘์—… (1)
    • ๐Ÿ“œ ์ฝ”๋”ฉํ…Œ์ŠคํŠธ (38)
      • BAEKJOON\์ˆ˜ํ•™ 1, ์ˆ˜ํ•™ 2 (8)
      • BAEKJOON\์žฌ๊ท€ (5)
      • BAEKJOON\๋ธŒ๋ฃจํŠธ ํฌ์Šค (3)
      • BAEKJOON\์ •๋ ฌ (1)
      • BAEKJOON\๋ฐฑํŠธ๋ž˜ํ‚น (5)
      • BAEKJOON\BFS, DFS (6)
      • BAEKJOON\์ด๋ถ„ํƒ์ƒ‰ (1)
      • BAEKJOON\๋‹ค์ด๋‚˜๋ฏน ํ”„๋กœ๊ทธ๋ž˜๋ฐ (9)
      • BAEKJOON\๊ทธ๋ฆฌ๋”” ์•Œ๊ณ ๋ฆฌ์ฆ˜ (0)
    • โœจ ISEUNGHAN (1)

์ธ๊ธฐ ๊ธ€

์ตœ๊ทผ ๊ธ€

์ „์ฒด
์˜ค๋Š˜
์–ด์ œ
๋ฐ˜์‘ํ˜•
hELLO ยท Designed By ์ •์ƒ์šฐ.
iseunghan
3) [test] ์ž…๋ ฅ๊ฐ’ ์ œํ•œํ•˜๊ธฐ (๋ฌด์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•) - ์Šคํ”„๋ง REST API
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”

๊ฐœ์ธ์ •๋ณด

  • ํ‹ฐ์Šคํ† ๋ฆฌ ํ™ˆ
  • ํฌ๋Ÿผ
  • ๋กœ๊ทธ์ธ

๋‹จ์ถ•ํ‚ค

๋‚ด ๋ธ”๋กœ๊ทธ

๋‚ด ๋ธ”๋กœ๊ทธ - ๊ด€๋ฆฌ์ž ํ™ˆ ์ „ํ™˜
Q
Q
์ƒˆ ๊ธ€ ์“ฐ๊ธฐ
W
W

๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๊ธ€

๊ธ€ ์ˆ˜์ • (๊ถŒํ•œ ์žˆ๋Š” ๊ฒฝ์šฐ)
E
E
๋Œ“๊ธ€ ์˜์—ญ์œผ๋กœ ์ด๋™
C
C

๋ชจ๋“  ์˜์—ญ

์ด ํŽ˜์ด์ง€์˜ URL ๋ณต์‚ฌ
S
S
๋งจ ์œ„๋กœ ์ด๋™
T
T
ํ‹ฐ์Šคํ† ๋ฆฌ ํ™ˆ ์ด๋™
H
H
๋‹จ์ถ•ํ‚ค ์•ˆ๋‚ด
Shift + /
โ‡ง + /

* ๋‹จ์ถ•ํ‚ค๋Š” ํ•œ๊ธ€/์˜๋ฌธ ๋Œ€์†Œ๋ฌธ์ž๋กœ ์ด์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ํ‹ฐ์Šคํ† ๋ฆฌ ๊ธฐ๋ณธ ๋„๋ฉ”์ธ์—์„œ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.