-
2장) 스프링 부트에서 테스트 코드를 작성하자Java & Spring/스프링 부트와 AWS로 혼자 구현하는 웹 서비스 2020. 6. 27. 01:24반응형
테스트 코드 소개
- TDD vs 단위 테스트
- TDD - 테스트가 주도하는 개발 (테스트 코드를 먼저 작성하는 것부터 시작)
- 레드 그린 사이클
- 항상 실패하는 테스트를 먼저 작성하고(Red)
- 테스트가 통과하는 프로덕션 코드를 작성하고(Green)
- 테스트가 통과하면 프로덕션 코드를 리팩토링함(Refactor)
- 단위 테스트 - TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것
- 따라서, 테스트 코드를 먼저 작성하지 않아도, 리팩토링을 포함하지 않아도 됨
- 개발 단계 초기에 문제를 발견하게 도와준다.
- 리팩토링을 하거나 라이브러리 업그레이드 등에서 기존 기능이 올바르게 작동하는지 확인할 수 있음
- 기능에 대한 불확실성을 감소
- 시스템에 대한 실제 문서를 제공
- 단위 테스트 장점
- 빠른 피드백
- 자동검증이 가능
- 개발자가 만든 기능을 안전하게 보호 ~> 새로운 기능이 추가될 때, 기존 기능이 잘 작동되는 것을 보장
- xUnit - 테스트 프레임워크
- x(개발 환경)에 따라 Unit 테스트를 도와주는 도구
- Java - JUnit, DB - DBUnit ...
Hello Controller 테스트 코드 작성하기
일반적으로 패키지 명은 웹 사이트 주소의 역순으로 만듬
- ex)admin.zin0.com 사이트 -> com.zin0.admin 패키지명
앞으로 만들 프로젝트의 메인 클래스가 될 Application 클래스를 만든다.
package com.zin0.book.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); } }
@SpringBootApplication - 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성을 모두 자동으로 설정
@SpringBootApplication이 있는 위치부터 설정을 읽어 나가기 때문에, 이 클래스는 항상 프로젝트의 최상단에 위치해야만 한다.
main 메소드의 SpringApplication.run으로 인해 내장 WAS를 실행
내장 WAS - 언제 어디서나 같은 환경에서 스프링 부트를 배포할 수 있음
컨트롤러와 관련된 것을 담을 패키지(여기서는 web)을 만든다.
HelloController 클래스를 만든다.
package com.zin0.book.springboot.web; import com.zin0.book.springboot.web.dto.HelloResponseDto; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String Hello() { return "hello"; } }
@RestController
- 컨트롤러를 JSON을 반환하는 컨트롤러로 만들어 준다.
- @ResponseBody를 각 메소드마다 선언했던 것을 한번에 사용할 수 있게 해준다고 생각하면 편함
@GetMapping
- Get 요청을 받을 수 있는 API를 만든다.
테스트 코드 검증을 위해, test에 HelloControllerTest 클래스를 생성한다.
검증할 것과 패키지 이름이 같아야함(실습하다가 에러를 반환하길래 패키지명에 오타가 있어서 바꿔줬더니 됐음)
보통 클래스 이름은 검증할 클래스 이름에 Test를 붙임
package com.zin0.book.springboot; import com.zin0.book.springboot.web.HelloController; 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.hamcrest.Matchers.is; 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; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @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)); } }
@RunWith(SpringRunner.class)
- 테스트 진행 시, JUnit에 내장된 실행자 외에 다른 실행자를 실행시킴
- 여기서는 SpringRunner라는 스프링 실행자를 사용
- 즉, 스프링 부트 테스트와 JUnit 사이에 연결자 역할
@WebMvcTest
- 여러 스프링 테스트 어노테이션 중, Web(Spring MVC)에 집중할 수 있는 어노테이션
- 선언할 경우 @Controller, @ControllerAdvice 등을 사용 가능
- 단, @Service, @Component, @Repository 등 사용 불가
- 위의 클래스는 Controller 클래스기 때문에 선언함
@Autowired
- 스프링이 관리하는 빈(Bean)을 주입 받음
@private MockMvc mvc
- 웹 API를 테스트할 때 사용 (Get, Post 등)
- 스프링 MVC 테스트의 시작점
mvc.perform(get("hello"))
- MockMvc를 통해 /hello 주소로 GET 요청을 보냄
- 체이닝이 지원 ~> 따라서 여러 검증 기능을 이어서 선언 가능
.andExpect(status().isOk())
- mvc.perform의 결과를 검증
- HTTP Header의 Status를 검증 ( 200, 404, 500 등 상태)
- 여기선 200인지 아닌지 검증
.andExpect(content().string(hello))
- mvc.perform의 결과 검증 ~> 응답 본문의 내용을 검증
- Controller에서 "hello"를 리턴하기 때문에 이 값이 맞는지 검증
롬복 소개 및 설치
- 롬복 - 자바 개발자들의 필수 라이브러리
- Getter, Setter, 기본 생성자, toString 등을 어노테이션으로 자동 생성해준다.
- build.gradle의 의존성 부분에 compile('org.projectlombok:lombok')을 추가
- [ Ctrl + Shift + A ]를 통해 Plugins를 선택해주고 Marketplace에서 롬복 플러그인을 인스톨해준다. (인스톨 후 재시작 필수) ~> 알림창 Enable 클릭 or "Setting -> Build -> Compiler -> Annotation Processors"에서 Enable 설정
- 롬복 - 자바 개발자들의 필수 라이브러리
Hello Controller 코드를 롬복으로 전환하기
테스트 코드가 작성한 코드를 지켜주기 때문에, 쉽게 변경할 수 있다.
web 패키지에 모든 응답 Dto를 담을 dto 패키지를 추가
HelloResponseDto 클래스를 생성
package com.zin0.book.springboot.web.dto; import lombok.Getter; import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor public class HelloResponseDto { private final String name; private final int amount; }
@Getter
- 선언된 모든 필드의 get 메소드를 생성
@RequiredArgsConstructor
- 선언된 모든 final 필드가 포함된 생성자를 생성
- final이 없는 필드는 생성자에 포함 X
HelloResponseDtoTest 클래스 생성
package com.zin0.book.springboot.dto; import com.zin0.book.springboot.web.dto.HelloResponseDto; 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); } }
aseertThat
- assertj라는 테스트 검증 라이브러리의 검증 메소드
- 검증하고 싶은 대상을 메소드 인자로 받음
- 메소드 체이닝 지원
Junit의 기본 assertThat이 아닌 assertj의 assertThat을 사용한 이유
- Junit의 assertThat을 쓰게 되면, is()와 같이 CoreMatchers 라이브러리가 필요하다.
- 하지만, Junit은 추가적 라이브러리가 필요 X
- assertj는 Junit에서 자동으로 라이브러리 등록을 해줌
HelloController에 ResponseDto 사용하도록 추가
package com.zin0.book.springboot.web; import com.zin0.book.springboot.web.dto.HelloResponseDto; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String Hello() { return "hello"; } @GetMapping("/hello/dto") public HelloResponseDto helloDto(@RequestParam("name") String name, @RequestParam("amount") int amount) { return new HelloResponseDto(name, amount); } }
@RequestParam
- 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션
HelloControllerTest에 테스트 코드 작성
package com.zin0.book.springboot; import com.zin0.book.springboot.web.HelloController; 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.hamcrest.Matchers.is; 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; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @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)); } @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))); } }
param
- API 테스트할 때 사용될 요청 파라미터를 설정
- 값은 String만 허용 (숫자/날짜 등 데이터도 문자열로 변경해주어야함)
jsonPath
- JSON 응답값을 필드별로 검증
- $를 기준으로 필드명 명시 해줘야함
추가)
main의 Application 클래스를 직접 확인하는 과정에서, Error를 반환
(8080포트가 이미 점유되었다고 반환)
cmd를 통해서 port를 죽이려고 시도했지만 실패했다.
/f를 써서 강제로 지우려고 했지만, 어떤 프로세스의 자식 프로세스라서 종료할 수 없다고 나왔다.
그래서, 연결 포트를 바꿔주기로 결정했다.
-> resources에 있는 applications.properties에
server.port = 원하는 포트번호
를 추가해서 연결 포트를 바꿔주었다.
(applications.properties가 없어서 만들어줬음)
그리고 신기한 점은 테스트 클래스에서 메소드를 만들 때, 한글로 적어도 지원이 된다는 것이었다..!!!
반응형'Java & Spring > 스프링 부트와 AWS로 혼자 구현하는 웹 서비스' 카테고리의 다른 글
5장) 스프링 시큐리티와 OAuth2.0으로 로그인 기능 구현하기 (0) 2020.07.05 4장 ) 머스테치로 화면 구성하기 (0) 2020.07.01 3장) 스프링 부트에서 JPA로 데이터베이스 다뤄보자 (0) 2020.06.28 1장) IntelliJ로 스프링부트 시작하기 (3) 2020.06.26 정리에 앞서... (0) 2020.06.25 - TDD vs 단위 테스트