본문 바로가기
언어공부/JAVA&SPRING

[패스트캠퍼스 수강 후기] 자바 인강 100% 환급 챌린지 36회차 미션

by hobbiz 2020. 9. 14.
반응형

오늘은 24, 25강을 수강해보았다.

 

Controller Test 마무리와 Repository Test 부분이었다.

 

테스트의 중요성을 알게해주는 강의였다.

 

 

 

 

 

 

 

*BirthdaySerializer

package com.fastcampus.javaallinone.project3.mycontact.configuration.serializer;

import com.fastcampus.javaallinone.project3.mycontact.domain.dto.Birthday;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.time.LocalDate;

public class BirthdaySerializer extends JsonSerializer<Birthday> {

    @Override
    public void serialize(Birthday value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value != null) {
            gen.writeObject(LocalDate.of(value.getYearOfBirthday(), value.getMonthOfBirthday(), value.getDayOfBirthday()));
        }
    }
}

 

 

*JsonConfig

package com.fastcampus.javaallinone.project3.mycontact.configuration;

import com.fastcampus.javaallinone.project3.mycontact.configuration.serializer.BirthdaySerializer;
import com.fastcampus.javaallinone.project3.mycontact.domain.dto.Birthday;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

@Configuration
public class JsonConfig {
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper){
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper);

        return converter;
    }

    @Bean
    public ObjectMapper objectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new BirthdayModule());
        objectMapper.registerModule(new JavaTimeModule());

        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        return objectMapper;
    }

    static class BirthdayModule extends SimpleModule{
        BirthdayModule(){
            super();
            addSerializer(Birthday.class, new BirthdaySerializer());
        }
    }

}

 

 

*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.repository.PersonRepository;
import com.fastcampus.javaallinone.project3.mycontact.service.PersonService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RequestMapping(value = "/api/person")
@RestController
@Slf4j
public class PersonController {
    @Autowired
    private PersonService personService;
    @Autowired
    private PersonRepository personRepository;

    @GetMapping("/{id}")
    public Person getPerson(@PathVariable Long id){
        return personService.getPerson(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void postPerson(@RequestBody 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);
    }


}

 

*Birthday

package com.fastcampus.javaallinone.project3.mycontact.domain.dto;

import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Embeddable;
import java.time.LocalDate;

// Entitiy 에 속해있는 DTO라는 것을 표시
@Embeddable
@NoArgsConstructor
@Data
public class Birthday {
    private Integer yearOfBirthday;
    private Integer monthOfBirthday;
    private Integer dayOfBirthday;

    private Birthday(LocalDate birthday) {
        this.yearOfBirthday = birthday.getYear();
        this.monthOfBirthday = birthday.getMonthValue();
        this.dayOfBirthday = birthday.getDayOfMonth();
    }

    public static Birthday of(LocalDate birthday){
        return new Birthday(birthday);
    }

}

 

 

*Person

package com.fastcampus.javaallinone.project3.mycontact.domain;

import com.fastcampus.javaallinone.project3.mycontact.controller.dto.PersonDto;
import com.fastcampus.javaallinone.project3.mycontact.domain.dto.Birthday;
import lombok.*;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Where;
import org.springframework.util.StringUtils;

import javax.persistence.*;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.time.LocalDate;

@Entity
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Data
@Where(clause = "deleted = false")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NonNull
    @NotEmpty
    @Column(nullable = false)
    private String name;

    private String hobby;

    private String address;

    @Valid
    @Embedded
    private Birthday birthday;

    private String job;

    private String phoneNumber;

    @ColumnDefault("0") //0=false
    private boolean deleted;

    public void set(PersonDto personDto){
         if(!StringUtils.isEmpty(personDto.getHobby())){
             this.setHobby(personDto.getHobby());
         }
         if(!StringUtils.isEmpty(personDto.getAddress())){
             this.setAddress(personDto.getAddress());
         }
         if(!StringUtils.isEmpty(personDto.getJob())){
             this.setJob(personDto.getJob());
         }
         if(!StringUtils.isEmpty(personDto.getPhoneNumber())){
             this.setPhoneNumber(personDto.getPhoneNumber());
         }

         if(personDto.getBirthday() != null){
             this.setBirthday(Birthday.of(personDto.getBirthday()));
         }
     }

    public Integer getAge(){
         if(this.birthday != null){
            return LocalDate.now().getYear() - this.getBirthday().getYearOfBirthday() + 1;
         }else{
             return null;
         }
    }

    public boolean isBirthdayToday(){
        return LocalDate.now().equals(LocalDate.of(this.getBirthday().getYearOfBirthday()
                                                , this.getBirthday().getMonthOfBirthday()
                                                , this.getBirthday().getDayOfBirthday()));
    }

}

 

 

*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.repository.PersonRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;

@Service
@Slf4j
public class PersonService {
    @Autowired
    private PersonRepository personRepository;

    public List<Person> getPeopleByName(String name){
//        List<Person> people = personRepository.findAll();
//
//        return people.stream().filter(person -> person.getName().equals(name)).collect(Collectors.toList());
        return personRepository.findByName(name);
    }

    @Transactional
    public Person getPerson(Long id){
//        Person person = personRepository.findById(id).get();
        Person person = personRepository.findById(id).orElse(null);

//        System.out.println("person : " + person);
        log.info("person ; {}", person); //log출력을 제한할 수 있는 장점이 있어서 print보다 많이 쓴다.
        return person;
    }

    @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(() -> new RuntimeException("아이디가 존재하지 않습니다."));

        if(!person.getName().equals(personDto.getName())){
            throw new RuntimeException("이름이 다릅니다.");
        }

        person.set(personDto);

        personRepository.save(person);
    }

    @Transactional
    public void modify(Long id, String name){
        Person person = personRepository.findById(id).orElseThrow(() -> new RuntimeException("아이디가 존재하지 않습니다."));
        person.setName(name);
        personRepository.save(person);
    }

    @Transactional
    public void delete(Long id){
        Person person = personRepository.findById(id).orElseThrow(() -> new RuntimeException("아이디가 존재하지 않습니다."));
        person.setDeleted(true);
        personRepository.save(person);
    }
}

 

 

*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.http.converter.json.MappingJackson2HttpMessageConverter;
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.util.NestedServletException;

import javax.transaction.Transactional;
import java.time.LocalDate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
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 PersonController personController;
    @Autowired
    private PersonRepository personRepository;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private MappingJackson2HttpMessageConverter messageConverter;

    private MockMvc mockMvc;

    //BeforeEach를 달면 매 테스트마다 한번씩 실행이 됨
    @BeforeEach
    void beforeEach(){
        mockMvc = MockMvcBuilders.standaloneSetup(personController).setMessageConverters(messageConverter).build();
    }

    @Test
    void getPerson() throws Exception {
        mockMvc.perform(
            MockMvcRequestBuilders.get("/api/person/1"))
                .andDo(print())
                .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)))
            .andDo(print())
            .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 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)))
                .andDo(print())
                .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");

        assertThrows(NestedServletException.class, () ->
                mockMvc.perform(
                    MockMvcRequestBuilders.put("/api/person/1")
                            .contentType(MediaType.APPLICATION_JSON_VALUE)
                            //수정하지 않는 항목도 들어있어야 결과값이 null로 바뀌지 않음
                            .content(toJsonString(dto)))
                    .andDo(print())
                    .andExpect(status().isOk()));
    }

    @Test
    void modifyName() throws Exception {
        mockMvc.perform(
            MockMvcRequestBuilders.patch("/api/person/1")
                .param("name","martinModified"))
            .andDo(print())
            .andExpect(status().isOk());

        assertThat(personRepository.findById(1L).get().getName()).isEqualTo("martinModified");
    }

    @Test
    void deletePerson() throws Exception {
        mockMvc.perform(
            MockMvcRequestBuilders.delete("/api/person/1"))
            .andDo(print())
            .andExpect(status().isOk());

        assertTrue(personRepository.findPeopleDeleted().stream().anyMatch(person -> person.getId().equals(1L)));
    }

    private String toJsonString(PersonDto personDto) throws JsonProcessingException {
        return objectMapper.writeValueAsString(personDto);
    }
}

 

 

*PersonRepositortTest

package com.fastcampus.javaallinone.project3.mycontact.repository;

import com.fastcampus.javaallinone.project3.mycontact.domain.Person;
import com.fastcampus.javaallinone.project3.mycontact.domain.dto.Birthday;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.transaction.Transactional;
import java.time.LocalDate;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;

@Transactional
@SpringBootTest
class PersonRepositoryTest {
    @Autowired
    private PersonRepository personRepository;

   @Test
    void findByName() {
       List<Person> people = personRepository.findByName("tony");
       assertThat(people.size()).isEqualTo(1);

       Person person = people.get(0);
       assertAll(
               () -> assertThat(person.getName()).isEqualTo("tony"),
               () -> assertThat(person.getHobby()).isEqualTo("reading"),
               () -> assertThat(person.getAddress()).isEqualTo("서울"),
               () -> assertThat(person.getBirthday()).isEqualTo(Birthday.of(LocalDate.of(1991,7,10))),
               () -> assertThat(person.getJob()).isEqualTo("officer"),
               () -> assertThat(person.getPhoneNumber()).isEqualTo("010-2222-5555"),
               () -> assertThat(person.isDeleted()).isEqualTo(false)
       );
   }

   @Test
    void findByNameIFDeleted(){
        List<Person> people = personRepository.findByName("andrew");

        assertThat(people.size()).isEqualTo(0);
   }

   @Test
    void findByMonthOfBirthday(){
       List<Person> people = personRepository.findByMonthOfBirthday(7);

       assertThat(people.size()).isEqualTo(2);
       assertAll(
               () ->  assertThat(people.get(0).getName()).isEqualTo("david"),
               () -> assertThat(people.get(1).getName()).isEqualTo("tony")
       );
   }

   @Test
    void findPeopleDeleted(){
       List<Person> people = personRepository.findPeopleDeleted();

       assertThat(people.size()).isEqualTo(1);
       assertThat(people.get(0).getName()).isEqualTo("andrew");
   }
}

 

 

*data.sql

insert into person(`id`, `name`, `year_of_birthday`, `month_of_birthday`, `day_of_birthday`) values (1, 'martin', 1991,8,15);
insert into person(`id`, `name`, `year_of_birthday`, `month_of_birthday`, `day_of_birthday`) values (2, 'david', 1992,7,21);
insert into person(`id`, `name`, `year_of_birthday`, `month_of_birthday`, `day_of_birthday`) values (3, 'dennis', 1993,10,15);
insert into person(`id`, `name`, `year_of_birthday`, `month_of_birthday`, `day_of_birthday`) values (4, 'sophia', 1994,8,31);
insert into person(`id`, `name`, `year_of_birthday`, `month_of_birthday`, `day_of_birthday`) values (5, 'benny', 1999,12,23);
insert into person(`id`, `name`, `year_of_birthday`, `month_of_birthday`, `day_of_birthday`, `job`, `hobby`, `phone_number`, `address`)
    values(6, 'tony', 1991, 7, 10, 'officer', 'reading', '010-2222-5555', '서울');
insert into person(`id`, `name`, `deleted`) values(7, 'andrew', true);

 

 

 

 

<추가공부>

Spring Boot 테스트 코드 작성

 

Spring Boot 테스트 코드 작성

1. TTD란?

"테스트 주도 개발: 테스트가 개발을 이끌어 나간다."라고 정의할 수 있다.

메소드 나 함수 같은 프로그램 모듈을 작성할 때

‘작성 종료조건을 먼저 정해놓고 코딩을 시작 한다’는 의미로 받아들이면 편하다.

  • RED : 항상 실패하는 테스트를 먼저 작성
  • GREEN : 테스트에 통과하는 프로덕션 코드 작성
  • REFACTOR : 테스트가 통과하면 프로덕션 코드를 리팩토링

위의 레드 그린 사이클 처럼 우선 테스트를 작성하고 그걸 통과하는 코드를 만들고 해당 과정을 반복하면서

제대로 동작하는지에 대한 피드백을 적극적으로 받는 것이다.

TDD를 왜 사용하는가?

  • 개발 단계 초기에 문제를 발견하게 해준다.
  • 추후에 코드를 리팩토링하거나 라이브러리 업그레이드 등에서 기존기능이 올바르게 작동하는지 확인할 수 있다.
  • 기능에 대한 불확실성을 감소시켜준다.
  • 시스템에 대한 실제 문서를 제공한다. 즉, 단위 테스트 자체가 문서로 사용할 수 있다.

2. Controller 테스트 코드 작성하기

Application Class 작성

package com.swchoi.webservice.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

Application 클래스는 앞으로 만들 프로젝트의 메인 클래스가 됩니다.

  • @SpringBootApplication으로 인해 스프링 부트의 자동설정, 스프링 Bean 일기기와 생성을 모두 자동으로 설정된다.
  • @SpringBootApplication이 있는 위치부터 설정을 읽어가기 때문에 이 클래스는 항상 프로젝트의 최상단에 위치해야 한다.
  • main 메소드에서 실행하는 SpringApplication.run으로 인해 내장 WAS(Web Application Server, 웹 어플리케이션 서버)를 실행한다.
  • 내장 WAS란 별도로 외부에 WAS를 두지 않고 애플리케이션을 실행할 때 내부에서 WAS를 실행하는 것을 이야기한다. 이렇게 되면 항상 서버에 톰캣(Tomcat)을 설치할 필요가 없게 되고, 스프링 부트로 만들어진 Jar 파일(실행 가능한 Java 패키징 파일)로 실행하면 된다.

HelloController Class 작성

package com.swchoi.webservice.springboot.web; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello(){ return "hello"; } }코드설명

  1. @RestController
    • 컨트롤러를 JSON을 반환하는 컨트롤러로 만들어 줍니다.
    • 예전에는 @ResponseBody를 각 메소드마다 선언했던 것을 한번에 사용할 수 있게 해준다.
  2. @GetMapping
    • HTTP Method인 Get인 요청을 받을 수 있는 API를 만들어 준다.
    • 예전에는 @RequestMapping(method = RequestMethod.GET)으로 사용되었습니다.

HelloControllerTest Class 작성

package com.swchoi.webservice.springboot.web; 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.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringRunner.class) @WebMvcTest(controllers = HelloController.class) public class HelloControllerTest { @Autowired private MockMvc mvc; @Test public void hello가_리턴된다() throws Exception { String hello = "hello"; mvc.perform(get("/hello")) .andExpect(status().isOk()) .andExpect(content().string(hello)); } }코드설명

  1. @RunWith(SpringRunner.class)
    • 테스트를 진행할 때 JUint에 내장된 실행자 외에 다른 실행자를 실행시킵니다.
    • 여기서는 SpringRunner라는 스프링 실행자를 사용합니다.
    • 즉, 스프링 부트 테스트와 JUnit 사이에 연갈자 역할을 합니다.
  2. @WebMvcTest
    • 여러 스프링 테스트 어노테이션 중, Web(Spring MVC)에 집중할 수 있는 어노테이션입니다.
    • 선언할 경우 @Controller, @ControllerAdvice 등을 사용할 수 있습니다.
    • 단, @Service, @Component, @Repository 등은 사용할 수 없다.
    • 여기서는 컨트롤러만 사용한다.
  3. @Autowired
    • 스프링이 관리하는 빈(Bean)을 주입 받는다.
  4. private MockMvc mvc
    • 웹 API를 테스트할 때 사용
    • 스프링 MVC 테스트의 시작점
    • 이 클래스를 통해 HTTP GET,POST 등에 대한 API 테스트를 할 수 있다.
  5. mvc.perform(get("/hello"))
    • MockMvc를 통해 /hello 주소로 HTTP GET 요청을 한다.
    • 체이닝이 지원되어 아래와 같이 여러 검증 기능을 이어서 선언할 수 있다.
  6. .andExpect(status().isOk())
    • mvc.perform의 결과를 검증한다.
    • HTTP Header의 Status를 검증합니다.
    • 우리가 흔히 알고 있는 200,404,500 등의 상태를 검증한다.
  7. .andExpect(content().string(hello))
    • mvc.perform의 결과를 검증한다.
    • 응답 본문의 내용을 검증한다.

3. Lombok 소개 및 설치하기

자바 개발자들의 필수 라이브러리 롬북(Lombok) 입니다.

build.gradle 파일에 추가 한다.

compile('org.projectlombok:lombok')


인텔리j lombok 플러그인 추가


롬북 설정 (Enalbe annotation processing 체크)

4. DTO 롬북 테스트 코드 작성하기

HelloResponseDto 클래스 생성

package com.swchoi.webservice.springboot.web.dto; import lombok.Getter; import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor public class HelloResponseDto { private final String name; private final int amount; }

코드설명

  1. @Getter
    • 선언된 모든 필드의 get 메소드를 생성해 줍니다.
  2. @RequiredArgsConstructor
    • 선언된 모든 final 필드가 포함된 생성자를 생성해 줍니다.
    • final이 없는 필드는 생성자에 포함되지 않습니다.

HelloResponseDtoTest 클래스 생성

package com.swchoi.webservice.springboot.web.dto; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class HelloResponseDtoTest { @Test public void 롬북_기능_테스트() { //given String name = "test"; int amount = 1000; //when HelloResponseDto dto = new HelloResponseDto(name,amount); //then assertThat(dto.getName()).isEqualTo(name); assertThat(dto.getAmount()).isEqualTo(amount); } }

코드설명

  1. assertThat
    • assertj라는 테스트 검증 라이브러리의 검증 메소드입니다.
  2. isEqualTo
    • 검증하고 싶은 대상을 메소드 인자로 받는다.
    • 메소드 체이닝이 지원되어 isEqualTo와 같이 메소드를 이어서 사용할 수 있다.

HelloController 메소드 추가

@GetMapping("/hello/dto") public HelloResponseDto helloDto(@RequestParam("name") String name, @RequestParam("amount") int amount) { return new HelloResponseDto(name, amount); }코드설명

  1. @RequestParam
    • 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션

HelloControllerTest 메소드 추가

@Test public void helloDto가_리턴된다() throws Exception { String name = "hello"; int amount = 1000; mvc.perform( get("/hello/dto") .param("name", name) .param("amount", String.valueOf(amount))) .andExpect(status().isOk()) .andExpect(jsonPath("$.name", is(name))) .andExpect(jsonPath("$.amount", is(amount))); }코드설명

  1. param
    • API 테스트할 때 사용될 요청 파라미터를 설정
    • 단, 값은 String만 허용됩니다.
    • 숫자/날짜 등의 데이터도 작성할 때는 문자열로 변경해야한 가능
  2. jsonPath
    • JSON 응답값을 필드별로 검증할 수 있는 메소드입니다.
    • $를 기준으로 필드명을 명시합니다.

출처: velog.io/@swchoi0329/Spring-Boot-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1

 

 

패스트캠퍼스 강의: https://bit.ly/3ilMbIO

 

Java 웹 개발 마스터 올인원 패키지 Online. | 패스트캠퍼스

자바 기초문법부터 프로젝트 실습까지 Java 문법부터 스프링/스프링부트, 트렌디한 기술인 JPA까지 모두 배우는 온라인 강의입니다.

www.fastcampus.co.kr

 

반응형

댓글