💐 Spring/Spring REST API

2) [test] JSON 응답으로 201이 나오는지 확인 - 스프링 REST API

2020. 9. 9. 11:30
목차
  1. Test 1) JSON 응답으로 201이 나오는지 확인.
  2. EventController 구현
  3. EventControllerTest 클래스 구현
반응형

Test 1) JSON 응답으로 201이 나오는지 확인.

- Location 헤더에 생성된 이벤트를 조회할 수 있는 URI 담겨 있는지 확인.
- id는 DB에 들어갈 때 자동 생성된 값으로 나오는지 확인.

 

일단 Test 클래스를 만든다

  • 테스트를 할 클래스에서 맥 기준으로 단축키 cmd + shift + T 를 눌러주면 테스트 클래스 생성해준다.

Location URI를 만들건데, 여기서는 HATEOAS가 제공하는 linkTo() , methodOn()을 사용할 것 이다.

객체를 JSON으로 변환 할때, ObjectMapper를 사용.

 

EventController 구현

package me.iseunghan.demoinflearnrestapi.events;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
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;

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

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

public class EventController {

    private final EventRepository eventRepository;

    public EventController(EventRepository eventRepository) {
        this.eventRepository = eventRepository;
    }

    @PostMapping
    public ResponseEntity createEvent(@RequestBody Event event) {
        Event newEvent = this.eventRepository.save(event);
        //link를 생성할땐,HATEOAS가 제공하는 linkTo(), methodOn()을 사용
        //methodOn()을 사용하지 않은 이유 : URL이 클래스 레벨에 붙었기 때문에 메소드를 호출 안해도 된다.
        URI createUri = linkTo(EventController.class).slash(newEvent.getId()).toUri();
        return ResponseEntity.created(createUri).body(event); //201응답을 Uri에 담아서 리턴시킨다.
    }
}

위에서 methodOn()을 안쓰고 linkTo(T class) 로 바로 들어간 이유는, 클래스 레벨에 붙은 @RequestMapping 때문이다.
Link를 만들때에는 @PostMapping("/api/events") 처럼 메소드레벨에 붙은 메소드를 호출하는 방식처럼 이루어지는데,

@Controller
public class Controller {
 ...

@PostMapping("/api/events")
public ResponseEntity createEvent(@RequestBody Event event){
    URI createUri = linkTo(methodOn(EventController.class).createEvent(null)).slash(newEvent.getId()).toUri();
    ...
}

만약 위와 같이 URL이 메소드 레벨에 붙었다면 methodOn() 까지 사용해야 한다.

 

EventControllerTest 클래스 구현

package me.iseunghan.demoinflearnrestapi.events;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
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 java.time.LocalDateTime;

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;

/**
 * MockMvc
 * - 스프링 MVC 테스트 핵심 클래스
 * - 웹서버를 띄우지 않고도 스프링 Mvc(DispatherServlet)가
 * 요청을 처리하는 과정을 확인할 수 있기 때문에 컨트롤러 테스트용으로 자주 쓰임.
 */
@RunWith(SpringRunner.class)
@WebMvcTest
public class EventControllerTests {

    @Autowired
    MockMvc mockMvc;

    @Autowired
    ObjectMapper objectMapper;

    //MockBean으로 등록하는 이유 : @WebMvcTest는 웹 관련 빈들만 등록해주기 때문에, 직접 등록해야한다.
    //(주의) 기존 빈을 테스트용 빈이 대체 한다. 
    @MockBean
    EventRepository eventRepository;

    @Test
    public void createEvent() throws Exception {
        Event event = Event.builder()
                .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("강남역 1번 출구")
                .build();
        event.setId(10);

        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)))//요청 본문에 json으로 변환후 넣어준다
            .andDo(print())//어떤 응답과 요청을 받았는지 확인가능.
            .andExpect(status().isCreated())//201요청이 들어왔는지?
            .andExpect(jsonPath("id").exists()) //json에 id가 있는지?
            .andExpect(header().exists(HttpHeaders.LOCATION)) //header에 Location정보가 담겨있는지
            .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_JSON_VALUE))//content-type에 "application/hal+json"가 나오는지?
        ;

    }

}

mockMvc.perform(post("/api/events/") post방식으로 "api/events/" URL를 넘겨서 호출한다.
.contentType(MediaType.APPLICATION_JSON) 본문 요청에 JSON을 담아서 보내고 있다고 명시
.accept(MediaType.HAL_JSON) HAL + JSON 타입으로 받겠다는 의미
.content(objectMapper.writeValueAsString(event)) 요청 본문에 event 객체를 JSON으로 변환해서 넣어준다.
.andDo(print()) 응답, 요청 print
.andExpect(status().isCreate()) 201요청이 들어왔는가
.andExpect(jsonPath("id").exists()) json에 id가 존재하는가?

 

@MockBean, Mokito.when().then()

Test도중 Spring에서 어느 의존성도 필요하지 않다면, Mockito의 @Mock을 사용하는 방법이 좋습니다. 왜냐하면, 우리가 바라는 빠르고 의존성없는 unit test로 가는 방향이기 때문입니다. 하지만, test 도중 spring container가 관리하는 bean들 중 하나라도 추가하거나 Mocking하고 싶다면 @MockBean을 선택할 수 있습니다. @MockBean은 ApplicationContext에 mock객체를 추가합니다. 그리고 mock객체는 같은 type의 이미 존재하는 Bean들을 대신할 것입니다. 하지만, @MockBean으로 대신된 context는 다른 context이므로 Spring Boot는 ApplicationContext를 test 초기화 중에 다시 로딩해야합니다.

 

테스트 실패

 

테스트 코드에서는 eventRepoitory를 Mock 객체를 전달했는데, @MockBean을 이용해서 생성한 객체의 메소드가 리턴 타입이 객체인 경우 null을 리턴하고, 기본 데이터 타입인 경우 기본 값을 리턴한다. 그리하여, 테스트에서 Controller의 UrI만드는 과정에서
newEvent.getId() 를 호출하는 과정에서 newEvent가 null이기 때문에 NullpointerException이 발생하여 테스트가 깨지게 된다.

이럴땐, Mockito를 이용한다..

Mockito는 Mock 객체의 메서드가 알맞은 값을 리턴하는 스텁을 만들 수 있는 기능을 제공하고 있다. 이 메서드는 when - then의 형식을 띄고
그리하여, when : eventRepository.save 메소드가 호출 될때! -> thenReturn : event 객체를 반환한다.

 

 

출처 : https://javacan.tistory.com/entry/MocktestUsingMockito

반응형
저작자표시 (새창열림)
  1. Test 1) JSON 응답으로 201이 나오는지 확인.
  2. EventController 구현
  3. EventControllerTest 클래스 구현
'💐 Spring/Spring REST API' 카테고리의 다른 글
  • 5) [test] 입력값 제한하기 (비즈니스 로직으로 검사) - 스프링 REST API
  • 4) [test] 입력값 제한하기 (Bad_Request 발생) - 스프링 REST API
  • 3) [test] 입력값 제한하기 (무시하는 방법) - 스프링 REST API
  • 1) Spring-Boot 프로젝트 만들기 - 스프링 REST API
iseunghan
iseunghan
꾸준하게 열심히..
iseunghan
iseunghan

공지사항

  • 어제보다 나은 오늘이 되기 위해 🔥
  • 분류 전체보기 (262)
    • 💐 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 (3)
    • 📚 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
2) [test] JSON 응답으로 201이 나오는지 확인 - 스프링 REST API
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.