Pageable : JPA Repository 로 조회할 때 페이징 작업
PagedResourcesAssembler : 페이지와 리소스를 EntityModel 로 변환
ㄴ 강의 영상에서 toResource() 사용 -> 버전 문제로 toModel 사용하여 문제 해결
*Event Controller 코드 추가
@GetMapping
public ResponseEntity queryEvents(Pageable pageable, PagedResourcesAssembler<Event> pagedResourcesAssembler) {
Page<Event> page = this.eventRepository.findAll(pageable);
var entityModels = pagedResourcesAssembler.toModel(page, EventResource::new);
entityModels.add(Link.of("/docs/index.html#resources-events-list").withRel("profile"));
return ResponseEntity.ok(entityModels);
}
Event Test 코드 추가
@Test
@DisplayName("30개의 이벤트를 10개씩 두번째 페이지 조회")
public void queryEvents() throws Exception {
//Given
IntStream.range(0, 30).forEach(this::generateEvent);
mockMvc.perform(get("/api/events")
.param("page", "1")
.param("size", "10")
.param("sort", "name,DESC"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("page").exists())
.andExpect(jsonPath("_embedded.eventList[0]._links.self").exists())
.andExpect(jsonPath("_links.self").exists())
.andExpect(jsonPath("_links.profile").exists())
.andDo(document("query-events"));
}
private void generateEvent(int index) {
Event event = Event.builder()
.name("event " + index)
.description("test event")
.build();
this.eventRepository.save(event);
}
생성과 동일한 방식으로 이벤트 핸들러 추가, profile 링크 추가 테스트 코드에 document 추가
Event Controller 코드 추가
@GetMapping("/{id}")
public ResponseEntity getEvent(@PathVariable Integer id) {
Optional<Event> optEvent = this.eventRepository.findById(id);
if (optEvent.isEmpty()) {
return ResponseEntity.notFound().build();
}
Event event = optEvent.get();
EventResource eventResource = new EventResource(event);
eventResource.add(Link.of("/docs/index.html#resources-events-get").withRel("profile"));
return ResponseEntity.ok(eventResource);
}
Event Test 코드 추가
id 값으로 조회 되거나 되지 않은 경우 2가지 테스트
@Test
@DisplayName("1개 조회")
public void getEvent() throws Exception {
Event event = generateEvent(100);
this.mockMvc.perform(get("/api/events/{id}", event.getId()))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("_links.self").exists())
.andExpect(jsonPath("_links.profile").exists())
.andDo(document("get-an-event"));
}
@Test
@DisplayName("잘못된 ID로 조회시 not found 응답")
public void notFount() throws Exception {
this.mockMvc.perform(get("/api/events/12345"))
.andExpect(status().isNotFound());
}
생성과 동일한 방식으로 이벤트 핸들러 추가, profile 링크 추가 테스트 코드에 document 추가
Event Controller 코드 추가
@PutMapping("/{id}")
public ResponseEntity updateEvent(@PathVariable Integer id, @RequestBody @Validated EventDto eventDto, Errors errors) {
Optional<Event> optionalEvent = this.eventRepository.findById(id);
if (optionalEvent.isEmpty()) {
return badRequest(errors);
}
if (errors.hasErrors()) {
return badRequest(errors);
}
this.eventValidator.validate(eventDto, errors);
if (errors.hasErrors()) {
return badRequest(errors);
}
Event event = optionalEvent.get();
modelMapper.map(eventDto, event);
Event updatedEvent = this.eventRepository.save(event);
EventResource eventResource = new EventResource(updatedEvent);
eventResource.add(Link.of("/docs/index.html#resources-events-update").withRel("profile"));
return ResponseEntity.ok(eventResource);
}
Event Test 코드 추가
@Test
@DisplayName("정상적으로 데이터 수정")
public void updateEvent() throws Exception {
Event event = this.generateEvent(200);
EventDto eventDto = this.modelMapper.map(event, EventDto.class);
String eventName = "Updated Event";
eventDto.setName(eventName);
this.mockMvc.perform(put("/api/events/{id}", event.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsString(eventDto)))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("name").value(eventName))
.andExpect(jsonPath("_links.self").exists());
}
@Test
@DisplayName("Event id 가 없는 데이터를 호출한 경우")
public void updateEvent404() throws Exception {
Event event = this.generateEvent(200);
EventDto eventDto = this.modelMapper.map(event, EventDto.class);
String eventName = "Updated Event";
eventDto.setName(eventName);
this.mockMvc.perform(put("/api/events/{id}", 12345)
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsString(eventDto)))
.andDo(print())
.andExpect(status().isBadRequest());
}
@Test
@DisplayName("수정하려는 데이터가 없는 데이터인 경우")
public void updateEvent400Empty() throws Exception {
Event event = this.generateEvent(200);
EventDto eventDto = new EventDto();
this.mockMvc.perform(put("/api/events/{id}", event.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsString(eventDto)))
.andDo(print())
.andExpect(status().isBadRequest());
}
@Test
@DisplayName("수정하려는 데이터 정상적이지 않은 데이터인 경우")
public void updateEvent400Wrong() throws Exception {
Event event = this.generateEvent(200);
EventDto eventDto = this.modelMapper.map(event, EventDto.class);
eventDto.setBasePrice(20000);
eventDto.setMaxPrice(100);
this.mockMvc.perform(put("/api/events/{id}", event.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsString(eventDto)))
.andDo(print())
.andExpect(status().isBadRequest());
}
private Event generateEvent(int index) {
Event event = Event.builder()
.name("event " + index)
.description("REST API")
.beginEnrollmentDateTime(LocalDateTime.of(2022, 3, 27, 11, 30))
.closeEnrollmentDateTime(LocalDateTime.of(2022, 3, 28, 11, 30))
.beginEventDateTime(LocalDateTime.of(2022, 4, 27, 11, 30))
.endEventDateTime(LocalDateTime.of(2022, 4, 28, 11, 30))
.basePrice(100)
.maxPrice(200)
.limitOfEnrollment(100)
.location("강남역")
.free(false)
.offline(true)
.eventStatus(EventStatus.DRAFT)
.build();
this.eventRepository.save(event);
return event;
}
공통으로 사용하는 annotation, autowired field 새로운 클래스 생성해서 옮기고
기존에 사용하던 EventControllerTests, IndexControllerTest 상속받아서 사용하기
강의 영상에서 사용하던 Junit4 @Ignore -> Junit5 @Disabled 대체하여 해결
부모 클래스 생성
@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@Import(RestDocsConfiguration.class)
@ActiveProfiles("test")
@Disabled
public class BaseControllerTest {
@Autowired
protected MockMvc mockMvc;
}
기존에 사용하던 클래스 코드 수정
public class EventControllerTests extends BaseControllerTest {
...생략
}
public class IndexControllerTest extends BaseControllerTest {
...생략
}