[인프런] 스프링 기반 REST API 개발 - 2. 이벤트 생성 API 개발

June·2021년 5월 12일
0

이벤트 API 테스트 클래스 생성

스프링 부트 슬라이스 테스트
● @WebMvcTest
○ MockMvc 빈을 자동 설정 해준다. 따라서 그냥 가져와서 쓰면 됨.
○ 웹 관련 빈만 등록해 준다. (슬라이스)

MockMvc
● 스프링 MVC 테스트 핵심 클래스
● 웹 서버를 띄우지 않고도 스프링 MVC (DispatcherServlet)가 요청을 처리하는 과정을 확인할 수 있기 때문에 컨트롤러 테스트용으로 자주 쓰임.

테스트 할 것
● 입력값들을 전달하면 JSON 응답으로 201이 나오는지 확인.
○ Location 헤더에 생성된 이벤트를 조회할 수 있는 URI 담겨 있는지 확인.
○ id는 DB에 들어갈 때 자동생성된 값으로 나오는지 확인
● 입력값으로 누가 id나 eventStatus, offline, free 이런 데이터까지 같이 주면?
○ Bad_Request로 응답 vs 받기로 받기로 한 값 이외는 무시
● 입력 데이터가 이상한 경우 Bad_Request로 응답
○ 입력값이 이상한 경우 에러
○ 비즈니스 로직으로 검사할 수 있는 에러
○ 에러 응답 메시지에 에러에 대한 정보가 있어야 한다.
● 비즈니스 로직 적용 됐는지 응답 메시지 확인
○ offline과 free 값 확인
● 응답에 HATEOA와 profile 관련 링크가 있는지 확인.
○ self (view)
○ update (만든 사람은 수정할 수 있으니까)
○ events (목록으로 가는 링크)
● API 문서 만들기
○ 요청 문서화
○ 응답 문서화
○ 링크 문서화
○ profile 링크 추가

/test/.../EventControllerTests

package com.whiteship.demoinflearnrestapi.events;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.StatusAssertions;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.StatusResultMatchers;

@RunWith(SpringRunner.class)
@WebMvcTest
public class EventControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void createEvent() throws Exception {
        mockMvc.perform(post("/api/events/")
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .accept(MediaTypes.HAL_JSON))
            .andExpect(status().isCreated());
    }
}

@WebMvcTest 어노테이션은 웹과 관련된 빈들이 생성된다.

MockMvc는 요청을 만들고, 응답을 검증할 수 있는 Spring mvc test에서 핵심이다. 서버를 띄우지 않기 때문에 좀 더 빠르고, 하지만 dispatcher servlet을 만들기 때문에 단위테스트보다는 빠르지 않다.

contentType -> 나는 요청에 JSON을 보내고 있다

accpt -> HAL_JSON을 원한다 (Hypermedia Application Language)

201 응답 받기

EventControllerTests

package com.whiteship.demoinflearnrestapi.events;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.StatusAssertions;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.StatusResultMatchers;

@RunWith(SpringRunner.class)
@WebMvcTest
public class EventControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void createEvent() throws Exception {
        mockMvc.perform(post("/api/events/")
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .accept(MediaTypes.HAL_JSON))
                    .andDo(print()) //실제 어떤 응답을 받았는지 출력해준다
                    .andExpect(status().isCreated())
                    .andExpect(jsonPath("id").exists());
    }
}

EventController

package com.whiteship.demoinflearnrestapi.events;

import java.net.URI;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

@Controller
public class EventController {

    @PostMapping("/api/events")
    public ResponseEntity createEvent() {

        URI createdUri = linkTo(methodOn(EventController.class).createEvent()).slash("{id}").toUri();
        return ResponseEntity.created(createdUri).build();
    }
}

URI를 남겨주고 있다.

@RestController
● @ResponseBody를 모든 메소드에 적용한 것과 동일하다.

ResponseEntity를 사용하는 이유
● 응답 코드, 헤더, 본문 모두 다루기 편한 API

Location URI 만들기
● HATEOS가 제공하는 linkTo(), methodOn() 사용

객체를 JSON으로 변환
● ObjectMapper 사용

테스트 할 것
● 입력값들을 전달하면 JSON 응답으로 201이 나오는지 확인.
○ Location 헤더에 생성된 이벤트를 조회할 수 있는 URI 담겨 있는지 확인
○ id는 DB에 들어갈 때 자동생성된 값으로 나오는지 확인

EventControllerTest

package com.whiteship.demoinflearnrestapi.events;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
//import org.springframework.security.oauth2.common.util.Jackson2JsonParser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import java.time.LocalDateTime;
import java.util.Set;
import java.util.stream.IntStream;

import static org.springframework.restdocs.headers.HeaderDocumentation.*;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
//import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static 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.*;

@RunWith(SpringRunner.class)
@WebMvcTest
public class EventControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Autowired
    ObjectMapper objectMapper;

    @Test
    public void createEvent() throws Exception {
        Event event = Event.builder()
            .name("Spring")
            .description("REST API Development with Spring")
            .beginEnrollmentDateTime(LocalDateTime.of(2018, 11, 23, 14, 21))
            .closeEnrollmentDateTime(LocalDateTime.of(2018, 11, 24, 14, 21))
            .beginEventDateTime(LocalDateTime.of(2018, 11, 25, 14, 21))
            .endEventDateTime(LocalDateTime.of(2018, 11, 26, 14, 21))
            .basePrice(100)
            .maxPrice(200)
            .limitOfEnrollment(100)
            .location("강남역 D2 스타텁 팩토리")
            .build();

        mockMvc.perform(post("/api/events/")
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaTypes.HAL_JSON)
            .content(objectMapper.writeValueAsString(event)))
            .andDo(print()) //실제 어떤 응답을 받았는지 출력해준다
            .andExpect(status().isCreated())
            .andExpect(jsonPath("id").exists());

    }
}

EventController

package com.whiteship.demoinflearnrestapi.events;

import java.net.URI;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

@Controller
public class EventController {

    @PostMapping("/api/events")
    @RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)
    public ResponseEntity createEvent(@RequestBody Event event) {

        URI createdUri = linkTo(EventController.class).slash("{id}").toUri();
        event.setId(10);
        return ResponseEntity.created(createdUri).body(event);
    }
}

0개의 댓글