[Spring REST API #2] Spring REST API 클래스 생성 및 201 테스트

Spring REST API 테스트 클래스 생성 및 201 응답

 

의존성

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-hateoas</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.modelmapper</groupId>
      <artifactId>modelmapper</artifactId>
      <version>2.3.1</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.restdocs</groupId>
      <artifactId>spring-restdocs-mockmvc</artifactId>
      <scope>test</scope>
    </dependency>
</dependencies>

 

소스 코드

package org.kyhslam.rest;

import lombok.*;
import javax.persistence.*;
import java.time.LocalDateTime;

@Builder @AllArgsConstructor @NoArgsConstructor
@Setter @Getter @EqualsAndHashCode(of = "id")
@Entity
public class Event {

    @Id @GeneratedValue
    private Integer id;
    private String name;
    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;
    private boolean offline;
    private boolean free;
    @Enumerated(EnumType.STRING)
    private EventStatus eventStatus;
}
  • 이벤트 엔티티의 소스코드

  • @Enumerated(EnumType.STRING) 을 통해 현재 Enum값이 문자열로 나타나도록 한다.

  • Default 값인 ORDINAL로 할 경우 EnumType의 순서가 바뀔 시 에러가 날 수 있으므로 문자열로 나타내게 하는것이 바람직하다

package org.kyhslam.rest;
public enum EventStatus {
    DRAFT, PUBLISHED, BEGAN_ENROLLMENT;
}
package org.kyhslam.rest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
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 java.net.URI;

@Controller
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_UTF8_VALUE)
public class EventController {

    @Autowired
    EventRepository eventRepository;


    @PostMapping
    public ResponseEntity createEvent(@RequestBody Event event){
        Event newEvent = eventRepository.save(event);

        URI createdUri = ControllerLinkBuilder.linkTo(EventController.class).slash(newEvent.getId()).toUri();
        return ResponseEntity.created(createdUri).body(event);
    }

}
  • HTTP요청을 처리하는 컨트롤러 이다.

  • @RequestMapping 어노테이션을 통해 이 컨트롤러가 /api/events/ URL과 매핑된 요청을 처리한다. 또한 HAL_JSON_UTF8_VALUE 형태로서 값을 반환한다.

  • @RequestBody를 통해 Body에 있는 정보가 자동적으로 Event 객체로 역질렬화되어 매핑된다.

  • HATEOS가 제공하는 linkTo 메서드를 통해 현 EventController의 URL에 대한 링크 정보를 쉽게 추가할 수 있다. 여기서는 save된 객체에서 자동적으로 할당된 ID가 포함된 URL을 Header에 넣어서 반환한다.

  • ResponseEntity는 HTTP 응답에 대한 정보를 가지고 있는 객체이다.

테스트 코드

package org.kyhslam.rest;

import com.fasterxml.jackson.databind.ObjectMapper;
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.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.time.LocalDateTime;

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

    @Autowired
    MockMvc mockMvc;

    @Autowired
    ObjectMapper objectMapper;

    @Test
    public void createEvent() throws Exception {

        Event event = Event.builder()
                .name("Spring")
                .description("REST API Development")
                .beginEnrollmentDateTime(LocalDateTime.of(2018,11,23,14,21))
                .closeEnrollmentDateTime(LocalDateTime.of(2018,11,23,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(MockMvcRequestBuilders.post("/api/events/")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .accept(MediaTypes.HAL_JSON)
                .content(objectMapper.writeValueAsString(event)))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isCreated());
    }

}
  • 빌더를 써서 이벤트 객체를 생성하여 테스트하는 코드.

  • 생성한 이벤트 객체를 /api/events/ 경로로 POST 메서드를 통해 이벤트 데이터를 생성하는 로직을 검사하는 테스트

  • 실제 웹 컨테이너를 구동하지 않고 MockMvc 를 써서 테스트를 진행할 수 있다. 웹 컨테이너가 구동되지 않는 만큼 테스트를 더 빨리 돌릴 수 있다.

  • ObjectMapper는 객체를 JSON으로 변환해 준다.

  • andDo(print()) 를 사용하면 테스트 콘솔창에 해당 RequestResponse를 볼 수 있어 디버깅하기 편하다.

  • andExpect 메서드를 통해서 어떤 응답이 올지 체크할 수 있다.

  • HttpHeaders에 등록된 상수들을 통해서 type-safe한 코드를 작성할 수 있다.

댓글

Designed by JB FACTORY