반응형
테스트 할 것
- 입력값으로 누가 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를 시켜주면 된다.
반응형