🌱 이번 장의 스터디 범위
- TDD와 단위 테스트란
- 스프링 부트 환경에서 테스트 코드를 작성하는 방법
- 자바의 필수 유틸 롬복의 사용법
🌱 테스트 코드 소개
- TDD : 테스트가 주도하는 개발로 테스트 코드를 먼저 작성하는 것부터 시작
- 단위 테스트 : TDD의 첫 번째 단위인 기능 단위의 테스트 코드를 작성하는 것, 순수하게 테스트 코드만 작성하는 것
🌱 테스트 코드의 이점
- 빠른 피드백
- 자동검증
- 개발자가 만든 기능을 안전하게 보호 (기본 기능이 잘 작동되는 것을 보장)
🌱 테스트 코드 작성을 도와주는 프레임워크
- JUnit - Java → 이를 앞으로 사용할 것!
- DBUnit - DB
- CppUnit - C++
- NUnit - .net
🌟 즉, 톰캣과 Postman을 통해 HTTP를 요청하고 요청 결과를 검증하기 보다는
테스트 프레임워크를 통해 테스트 코드를 작성해 자동으로 코드를 검증하는 것!
🌱 HelloController 테스트 코드 작성하기 - 테스트 코드로 검증
- 패키지 생성 : src - main - java - [New - Package] - 웹사이트 주소의 역순으로 패키지명 설정
- 패키지 아래 Java 클래스 생성 : java - [New - Java class] - Application 이름으로 프로젝트의 메인 클래스 생성
- 클래스 코드 작성 : 윈도우의 패키지 가져오기의 경우 [Alt + Enter]
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/* @SpringBootApplication으로 인해 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성을 모두 자동으로 설정
@SpringBootApplication이 있는 위치부터 설정을 읽어가므로 이 클래스는 항상 프로젝트의 최상단에 위치 */
@SpringBootApplication
public class Application { // 프로젝트의 메인 클래스
public static void main(String[] args) {
/* main 메소드에서 실행하는 SpringApplication.run으로 인해 내장 WAS(웹 애플리케이션 서버)를 실행
이로 인해 톰캣 설치가 필요없고, 스프링 부트로 만들어진 Jar 파일로 실행
스프링 부트는 내장 WAS를 사용하는 것을 권장 : 언제 어디서나 같은 환경에서 스프링 부트를 배포할 수 있기 때문 */
SpringApplication.run(Application.class, args);
}
}
- 패키지 하위에 web 패키지 생성 : com.gagyeong.book.springboot - [New - Package] - web 이름으로 패키지명 설정
- 앞으로 web에 컨트롤러와 관련된 클래스들을 모두 담을 것
- 테스트해볼 컨트롤러 생성 : web - [New - Java class] - HelloController 이름으로 클래스 생성
- 간단한 API 생성
// HelloController.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // JSON으로 반환하는 컨트롤러 생성
public class HelloController {
@GetMapping("/hello") // HTTP Method인 Get의 요청을 받을 수 있는 API 생성
public String hello() {
return "hello";
}
}
- WAS를 실행하지 않고, 테스트 코드 검증 : src/test/java 디렉토리에 앞에서 생성했던 패키지를 그대로 다시 생성
- 테스트 코드를 작성할 클래스 생성 : web - [New - Java class] - HelloControllerTest 이름으로 클래스 생성
- 생성된 클래스에 테스트 코드 추가
// HelloControllerTest.java
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 org.springframework.test.web.servlet.ResultActions;
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;
// 스프링 부트 테스트와 JUnit 사이에 연결자 역할
@RunWith(SpringRunner.class)
// Web(Spring MVC)에 집중할 수 있는 어노테이션
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {
// 스프링이 관리하는 빈을 주입 받음
@Autowired
// 웹 API를 테스트할 때 사용 (스프링 MVC 테스트의 시작점 - 이를 통해 HTTP GET, POST 등에 대한 API 테스트 가능)
private MockMvc mvc;
@Test
public void hello가_리턴된다() throws Exception {
String hello = "hello";
// MockMvc를 통해 /hello 주소로 HTTP GET 요청
mvc.perform(get("/hello"))
// mvc.perform의 결과를 검증1 - HTTP Header의 Status 검증 (Ok이므로 200인지 검증)
.andExpect(status().isOk())
// mvc.perform의 결과를 검증2 - 응답 본문의 내용을 검증 (Controller에서 "hello"를 리턴하여 이 값이 맞는지 검증)
.andExpect(content().string(hello));
}
}
- 테스트 코드 실행 : HelloControllerTest.java에서 메소드 왼쪽의 화살표 클릭
- 테스트 통과 : .andExpect(status().isOk())와 .andExpect(content().string(hello))가 모두 테스트 통과했음을 의미
🌟 메인 클래스인 Application.java을 만든 후,
Controller인 HelloController.java를 만들어 API를 생성하고 테스트 코드인 HelloControllerTest.java를 이용해 검증
🌱 Hello Controller 테스트 코드 작성하기 - 수동으로 프로젝트를 실행해 검증
- 수동으로 실행해서 정상적으로 값이 출력되는지 확인 : Application.java에서 main 메소드 왼쪽의 화살표 클릭
- 톰캣 서버 8080 포트로 실행되었다는 것이 로그에 출력
- 'localhost:8080/hello' 로 접속해 확인 : 테스트 코드의 결과 같은 것을 알 수 있음
🌟 브라우저로 한 번씩 검증은 하시되, 테스트 코드는 꼭 따라 해야 하며, 절대 수동으로 검증하고 테스트 코드를 작성하지 말자!
테스트 코드로 먼저 검증 후, 정말 못 믿겠다는 생각이 들 땐 프로젝트를 실행해 확인
🌱 롬복 소개 및 설치하기
- 롬복 : 자바 개발자들의 필수 라이브러리
- 롬복의 역할 : 자바 개발할 때 자주 사용하는 코드 Getter, Setter, 기본생성자, toString 등을 어노테이션으로 자동 생성
- 인텔리제이의 경우 플러그인을 통해 쉽게 롬복 설정 가능
- 프로젝트에 롬복 추가 : build.gradle에 코드 추가 및 새로고침을 통한 라이브러리 받기
// build.gradle
dependencies { // 다운그레이드 시 complile 사용 가능
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.projectlombok:lombok')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
- 롬복 플러그인 설치 검색 : [Ctrl + Shift + A] 를 사용해 Action 검색창을 열어 plugins 검색 후 클릭
- 롬복 플러그인 설치 : [Marketplace] 탭에 lombok을 검색해 install
- 인텔리제이 다시 시작하여 설치한 플러그인 적용
- 롬복에 대한 설정 : File - Setting - ··· - Enable annotation processiong 체크
🌟 롬복 플러그인 설치는 한 번만 하면 되지만,
build.gradle에 라이브러리를 추가하는 것과 Enable annotation processiong을 체크하는 것은 프로젝트마다 진행해야 함
🌱 HelloController 코드를 롬복으로 전환하기
- 테스트 코드가 우리의 코드를 지켜주므로 롬복으로 변경을 하고 문제가 생기는지 테스트 코드로 검증
- main - web 패키지에 dto 패키지 추가 : 모든 응답 Dto는 이 Dto 패키지에 추가
- 롬복을 적용할 HelloResponseDto 클래스 생성 및 코드 작성
// HelloResponseDto.java
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter // 선언된 모든 필드의 get 메소드를 생성
@RequiredArgsConstructor // 선언된 모든 final 필드가 포함된 생성자들 생성
public class HelloResponseDto {
private final String name;
private final int amount;
}
- test - web에 dto 패키지 추가
- HelloResponseDto에 적용된 롬복이 잘 작동하는지 HelloResponseDtoTest 클래스 생성 및 간단한 테스트 코드 작성
// HelloResponseDtoTest.java
import org.junit.Test;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
public class HelloResponseDtoTest {
@Test
public void 롬복_기능_테스트() {
// given
String name = "test";
int amount = 1000;
// when
HelloResponseDto dto = new HelloResponseDto(name, amount);
// then
/* assertThat : assertj라는 테스트 검증 라이브러리의 검증 메소드
isEqualTo : assertg의 동등 비교 메소드
-> assertThat에 있는 값과 isEqualTo 값을 비교해서 같을 때 성공 */
assertThat(dto.getName()).isEqualTo(name);
assertThat(dto.getAmount()).isEqualTo(amount);
}
}
- 정상 수행 확인 : 롬복의 @Getter로 get 메소드, @RequiredArgsConstructor로 생성자가 자동으로 생성된 것!
🌟 롬복을 적용한 HelloResponseDto.java를 만들고 테스트 코드인 HelloResponseDtoTest.java를 이용해 검증
- 위와 마찬가지로 HelloController에도 새로 만든 HelloResponseDto를 사용하도록 코드 추가
// HelloController.java
import com.gagyeong.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")
/* RequestParam : 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션
name (@RequestParam("name")) 이란 이름으로 넘긴 파라미터를 메소드 파라미터 name(String name)에 저장 */
public HelloResponseDto helloDto(@RequestParam("name") String name,
@RequestParam("amount") int amount) {
return new HelloResponseDto(name, amount);
}
}
- name과 amount는 API를 호출하는 곳에서 넘겨준 값들이므로 추가된 API를 테스트하는 코드를 HelloControllerTest에 추가
// HelloControllerTest.java
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.jsonPath;
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));
}
@Test
public void helloDto가_리턴된다() throws Exception {
String name = "hello";
int amount = 1000;
mvc.perform(get("/hello/dto")
// param : API 테스트할 때 사용될 파라미터를 설정. 단, String 값만 허용
.param("name", name)
.param("amount", String.valueOf(amount)))
.andExpect(status().isOk())
// jsonPath : JSON 응답값을 필드별로 검증할 수 있는 메소드. $를 기준으로 필드명 명시
.andExpect(jsonPath("$.name", is(name)))
.andExpect(jsonPath("$.amount", is(amount)));
}
}
- JSON이 리턴되는 API 테스트 통과 확인
🌟 롬복을 적용한 HelloResponseDto.java를 이용해 HelloController에 사용하고
테스트 코드인 HelloControllerTest.java를 이용해 검증
🌱 참조했던 링크, 문서
'Java-Spring > 스프링 부트와 AWS로 혼자 구현하는 웹 서비스' 카테고리의 다른 글
[Spring Boot] 03장. 스프링 부트에서 JPA로 데이터베이스 다뤄보자 - JPA Auditing (0) | 2021.10.06 |
---|---|
[Spring Boot] 03장. 스프링 부트에서 JPA로 데이터베이스 다뤄보자 - JPA (0) | 2021.10.04 |
[Spring Boot] 01장. 인텔리제이로 스프링 부트 시작하기 (0) | 2021.10.02 |
[Spring Boot] 00장. MVC의 역할과 실행 흐름 (0) | 2021.10.01 |
[Spring Boot] 00장. 뷰 템플릿과 MVC 패턴 (0) | 2021.10.01 |