반응형
오늘은 List Api & Paging 강의 및 마무리 강의를 공부해보았다.
벌써 마무리라니... 나는 아직 모르는게 너무 많은데...
거의 받아쓰기 느낌이었지만, 그래도 끝까지 한번 훑을 수 있어서 좋았다.
다음에 또 배울 때 들어본 느낌이라도 나겠지...
그동안 학습한 내용에 대한 요약
<코드 정리>
*PersonController
package com.fastcampus.javaallinone.project3.mycontact.controller;
import com.fastcampus.javaallinone.project3.mycontact.controller.dto.PersonDto;
import com.fastcampus.javaallinone.project3.mycontact.domain.Person;
import com.fastcampus.javaallinone.project3.mycontact.service.PersonService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RequestMapping(value = "/api/person")
@RestController
@Slf4j
public class PersonController {
@Autowired
private PersonService personService;
@GetMapping
public Page<Person> getAll(@PageableDefault Pageable pageable){
return personService.getAll(pageable);
}
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id){
return personService.getPerson(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void postPerson(@RequestBody @Valid PersonDto personDto) {
personService.put(personDto);
}
@PutMapping("/{id}")
public void modifyPerson(@PathVariable Long id, @RequestBody PersonDto personDto){
personService.modify(id, personDto);
}
@PatchMapping("/{id}")
public void modifyPerson(@PathVariable Long id, String name){
personService.modify(id, name);
}
@DeleteMapping("/{id}")
public void deletePerson(@PathVariable Long id){
personService.delete(id);
}
}
*PersonControllerTest
package com.fastcampus.javaallinone.project3.mycontact.controller;
import com.fastcampus.javaallinone.project3.mycontact.controller.dto.PersonDto;
import com.fastcampus.javaallinone.project3.mycontact.domain.Person;
import com.fastcampus.javaallinone.project3.mycontact.domain.dto.Birthday;
import com.fastcampus.javaallinone.project3.mycontact.repository.PersonRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import javax.transaction.Transactional;
import java.time.LocalDate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertTrue;
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;
@Slf4j
@SpringBootTest
@Transactional
class PersonControllerTest {
@Autowired
private PersonRepository personRepository;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
//BeforeEach를 달면 매 테스트마다 한번씩 실행이 됨
@BeforeEach
void beforeEach(){
mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.alwaysDo(print())
.build();
}
@Test
void getAll() throws Exception{
mockMvc.perform(
MockMvcRequestBuilders.get("/api/person")
.param("page", "1")
.param("size", "2"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.totalPages").value(3))
.andExpect(jsonPath("$.totalElements").value(6))
.andExpect(jsonPath("$.numberOfElements").value(2))
.andExpect(jsonPath("$.content.[0].name").value("dennis"))
.andExpect(jsonPath("$.content.[1].name").value("sophia"));
}
@Test
void getPerson() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.get("/api/person/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("martin"))
.andExpect(jsonPath("$.hobby").isEmpty())
.andExpect(jsonPath("$.address").isEmpty())
.andExpect(jsonPath("$.birthday").value("1991-08-15"))
.andExpect(jsonPath("$.job").isEmpty())
.andExpect(jsonPath("$.phoneNumber").isEmpty())
.andExpect(jsonPath("$.deleted").value(false))
.andExpect(jsonPath("$.age").exists()) //숫자를 넣을 경우 연도가 바뀜에 따라 테스트가 실패할 수 있어서 존재여부만 확인함
.andExpect(jsonPath("$.birthdayToday").isBoolean());
}
@Test
void postPerson() throws Exception{
PersonDto dto = PersonDto.of("martin", "programming", "판교", LocalDate.now(), "programmer", "010-1111-2222");
mockMvc.perform(
MockMvcRequestBuilders.post("/api/person")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(toJsonString(dto)))
.andExpect(status().isCreated());
Person result = personRepository.findAll(Sort.by(Sort.Direction.DESC, "id")).get(0);
assertAll(
() -> assertThat(result.getName()).isEqualTo("martin"),
() -> assertThat(result.getHobby()).isEqualTo("programming"),
() -> assertThat(result.getAddress()).isEqualTo("판교"),
() -> assertThat(result.getBirthday()).isEqualTo(Birthday.of(LocalDate.now())),
() -> assertThat(result.getJob()).isEqualTo("programmer"),
() -> assertThat(result.getPhoneNumber()).isEqualTo("010-1111-2222")
);
}
@Test
void postPersonIfNameIsNull() throws Exception {
PersonDto dto = new PersonDto();
mockMvc.perform(
MockMvcRequestBuilders.post("/api/person")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(toJsonString(dto)))
.andExpect(jsonPath("$.code").value(400))
.andExpect(jsonPath("$.message").value("이름은 필수값입니다"));
}
@Test
void postPersonIfNameIsEmpty() throws Exception{
PersonDto dto = new PersonDto();
dto.setName("");
mockMvc.perform(
MockMvcRequestBuilders.post("/api/person")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.contentType(toJsonString(dto)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value(400))
.andExpect(jsonPath("$.message").value("이름은 필수값입니다"));
}
@Test
void postPersonIfNameIsBlankString() throws Exception {
PersonDto dto = new PersonDto();
dto.setName(" ");
mockMvc.perform(
MockMvcRequestBuilders.post("/api/person")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(toJsonString(dto)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value(400))
.andExpect(jsonPath("$.message").value("이름은 필수값입니다"));
}
@Test
void modifyPerson() throws Exception{
PersonDto dto = PersonDto.of("martin", "programming", "판교", LocalDate.now(), "programmer", "010-1111-2222");
mockMvc.perform(
MockMvcRequestBuilders.put("/api/person/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
//수정하지 않는 항목도 들어있어야 결과값이 null로 바뀌지 않음
.content(toJsonString(dto)))
.andExpect(status().isOk());
Person result = personRepository.findById(1L).get();
assertAll(
() -> assertThat(result.getName()).isEqualTo("martin"),
() -> assertThat(result.getHobby()).isEqualTo("programming"),
() -> assertThat(result.getAddress()).isEqualTo("판교"),
() -> assertThat(result.getBirthday()).isEqualTo(Birthday.of(LocalDate.now())),
() -> assertThat(result.getJob()).isEqualTo("programmer"),
() -> assertThat(result.getPhoneNumber()).isEqualTo("010-1111-2222")
);
}
@Test
void modifyPersonIfNameIsDifferent() throws Exception{
PersonDto dto = PersonDto.of("james", "programming", "판교", LocalDate.now(), "programmer", "010-1111-2222");
mockMvc.perform(
MockMvcRequestBuilders.put("/api/person/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
//수정하지 않는 항목도 들어있어야 결과값이 null로 바뀌지 않음
.content(toJsonString(dto)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value(400))
.andExpect(jsonPath("$.message").value("이름 변경이 허용되지 않습니다."));
}
@Test
void modifyPersonIfPersonNotFound() throws Exception {
PersonDto dto = PersonDto.of("martin", "programming", "판교", LocalDate.now(), "programmer", "010-1111-2222");
mockMvc.perform(
MockMvcRequestBuilders.put("/api/person/10")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(toJsonString(dto)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value(400))
.andExpect(jsonPath("$.message").value("Person Entity가 존재하지 않습니다."));
}
@Test
void modifyName() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.patch("/api/person/1")
.param("name","martinModified"))
.andExpect(status().isOk());
assertThat(personRepository.findById(1L).get().getName()).isEqualTo("martinModified");
}
@Test
void deletePerson() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.delete("/api/person/1"))
.andExpect(status().isOk());
assertTrue(personRepository.findPeopleDeleted().stream().anyMatch(person -> person.getId().equals(1L)));
}
private String toJsonString(PersonDto personDto) throws JsonProcessingException {
return objectMapper.writeValueAsString(personDto);
}
}
*PersonService
package com.fastcampus.javaallinone.project3.mycontact.service;
import com.fastcampus.javaallinone.project3.mycontact.controller.dto.PersonDto;
import com.fastcampus.javaallinone.project3.mycontact.domain.Person;
import com.fastcampus.javaallinone.project3.mycontact.exception.PersonNotFoundException;
import com.fastcampus.javaallinone.project3.mycontact.exception.RenameIsNotPermitedException;
import com.fastcampus.javaallinone.project3.mycontact.repository.PersonRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Slf4j
public class PersonService {
@Autowired
private PersonRepository personRepository;
public Page<Person> getAll(Pageable pageable) {
return personRepository.findAll(pageable);
}
public List<Person> getPeopleByName(String name){
return personRepository.findByName(name);
}
@Transactional(readOnly = true)
public Person getPerson(Long id){
return personRepository.findById(id).orElse(null);
}
@Transactional
public void put(PersonDto personDto){
Person person = new Person();
person.set(personDto);
person.setName(personDto.getName());
personRepository.save(person);
}
@Transactional
public void modify(Long id, PersonDto personDto){
Person person = personRepository.findById(id).orElseThrow(PersonNotFoundException::new);
if(!person.getName().equals(personDto.getName())){
throw new RenameIsNotPermitedException();
}
person.set(personDto);
personRepository.save(person);
}
@Transactional
public void modify(Long id, String name){
Person person = personRepository.findById(id).orElseThrow(PersonNotFoundException::new);
person.setName(name);
personRepository.save(person);
}
@Transactional
public void delete(Long id){
Person person = personRepository.findById(id).orElseThrow(PersonNotFoundException::new);
person.setDeleted(true);
personRepository.save(person);
}
}
*PersonServiceTest
package com.fastcampus.javaallinone.project3.mycontact.service;
import com.fastcampus.javaallinone.project3.mycontact.controller.dto.PersonDto;
import com.fastcampus.javaallinone.project3.mycontact.domain.Person;
import com.fastcampus.javaallinone.project3.mycontact.domain.dto.Birthday;
import com.fastcampus.javaallinone.project3.mycontact.exception.PersonNotFoundException;
import com.fastcampus.javaallinone.project3.mycontact.exception.RenameIsNotPermitedException;
import com.fastcampus.javaallinone.project3.mycontact.repository.PersonRepository;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatcher;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class personServiceTest {
@InjectMocks //테스트의 대상이 되는 클래스에 붙여줌
private PersonService personService;
@Mock //이 클래스를 Mock으로 만들어서 위 클래스에 주입
private PersonRepository personRepository;
@Test
void getAll(){
when(personRepository.findAll(any(Pageable.class)))
.thenReturn(new PageImpl<>(Lists.newArrayList(new Person("martin"), new Person("dennis"), new Person("tony"))));
Page<Person> result = personService.getAll(PageRequest.of(0, 3));
assertThat(result.getNumberOfElements()).isEqualTo(3);
assertThat(result.getContent().get(0).getName()).isEqualTo("martin");
assertThat(result.getContent().get(1).getName()).isEqualTo("dennis");
assertThat(result.getContent().get(2).getName()).isEqualTo("tony");
}
@Test
void getPeopleByName(){
when(personRepository.findByName("martin"))
.thenReturn(Lists.newArrayList(new Person("martin")));
List<Person> result = personService.getPeopleByName("martin");
assertThat(result.size()).isEqualTo(1);
assertThat(result.get(0).getName()).isEqualTo("martin");
}
@Test
void getPerson() {
when(personRepository.findById(1L))
.thenReturn(Optional.of(new Person("martin")));
Person person = personService.getPerson(1L);
assertThat(person.getName()).isEqualTo("martin");
}
@Test
void getPersonIfNotFound() {
when(personRepository.findById(1L))
.thenReturn(Optional.empty());
Person person = personService.getPerson(1L);
assertThat(person).isNull();
}
@Test
void put(){
personService.put(mockPersonDto());
//void 메서드를 mock 테스트 할 떄 사용
verify(personRepository, times(1)).save(argThat(new IsPersonWillBeInserted()));
}
@Test
void modifyPersonNotFound(){
when(personRepository.findById(1L))
.thenReturn(Optional.empty());
assertThrows(PersonNotFoundException.class, () -> personService.modify(1L, mockPersonDto()));
}
@Test
void modifyIfNameIsDifferent(){
when(personRepository.findById(1L))
.thenReturn(Optional.of(new Person("tony")));
assertThrows(RenameIsNotPermitedException.class, () -> personService.modify(1L, mockPersonDto()));
}
@Test
void modify(){
when(personRepository.findById(1L))
.thenReturn(Optional.of(new Person("martin")));
personService.modify(1L, mockPersonDto());
verify(personRepository, times(1)).save(argThat(new IsPersonWillBeUpdated()));
}
@Test
void modifyByNameIfPersonNotFound() {
when(personRepository.findById(1L))
.thenReturn(Optional.empty());
assertThrows(PersonNotFoundException.class, () -> personService.modify(1L, "daniel"));
}
@Test
void modifyByName(){
when(personRepository.findById(1L))
.thenReturn(Optional.of(new Person("martin")));
personService.modify(1L, "daniel");
verify(personRepository, times(1)).save(argThat(new IsNameWillBeUpdated()));
}
@Test
void deleteIfPersonNotFound() {
when(personRepository.findById(1L))
.thenReturn(Optional.empty());
assertThrows(PersonNotFoundException.class, () -> personService.delete(1L));
}
@Test
void delete() {
when(personRepository.findById(1L))
.thenReturn(Optional.of(new Person("martin")));
personService.delete(1L);
verify(personRepository, times(1)).save(argThat(new IsPersonWillBeDeleted()));
}
private PersonDto mockPersonDto(){
return PersonDto.of("martin", "programming", "판교", LocalDate.now(), "programmer", "010-1111-2222");
}
private static class IsPersonWillBeUpdated implements ArgumentMatcher<Person> {
@Override
public boolean matches(Person person) {
return equals(person.getName(), "martin")
&& equals(person.getAddress(), "판교")
&& equals(person.getHobby(), "programming")
&& equals(person.getBirthday(), Birthday.of(LocalDate.now()))
&& equals(person.getJob(), "programmer")
&& equals(person.getPhoneNumber(), "010-1111-2222");
}
private boolean equals(Object actual, Object expected){
return expected.equals(actual);
}
}
private static class IsPersonWillBeInserted implements ArgumentMatcher<Person> {
@Override
public boolean matches(Person person) {
return equals(person.getName(), "martin")
&& equals(person.getAddress(), "판교")
&& equals(person.getHobby(), "programming")
&& equals(person.getBirthday(), Birthday.of(LocalDate.now()))
&& equals(person.getJob(), "programmer")
&& equals(person.getPhoneNumber(), "010-1111-2222");
}
private boolean equals(Object actual, Object expected) {
return expected.equals(actual);
}
}
private static class IsNameWillBeUpdated implements ArgumentMatcher<Person>{
@Override
public boolean matches(Person person) {
return person.getName().equals("daniel");
}
}
private static class IsPersonWillBeDeleted implements ArgumentMatcher<Person> {
@Override
public boolean matches(Person person) {
return person.isDeleted();
}
}
}
패스트캠퍼스 강의: https://bit.ly/3ilMbIO
반응형
'언어공부 > JAVA&SPRING' 카테고리의 다른 글
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 43회차 미션 (0) | 2020.09.21 |
---|---|
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 42회차 미션 (0) | 2020.09.20 |
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 40회차 미션 (0) | 2020.09.18 |
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 39회차 미션 (0) | 2020.09.17 |
[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 38회차 미션 (0) | 2020.09.16 |
댓글