1. 테스트 코드
- TDD : 테스트가 주도하는 개발로, 테스트 코드를 먼저 작성하는 것
- 단위 테스트 : TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것
TDD와 달리 테스트 코드를 꼭 먼저 작성하지 않아도 되며, 리팩토링도 포함되지 않음
2. 단위 테스트
- 개발단계 초기에 문제 발견을 도와줌
- 개발자가 나중에 코드를 리팩토링하거나 라이브러리 업그레이드 등에서 기존 기능이 올바르게 작동하는지 확인할 수 있음
- 기능에 대한 불확실성을 감소시킬 수 있음
- 시스템에 대한 실제 문서를 제공하며, 단위 테스트 자체가 문서로 사용할 수 있음
3. 테스트 코드 작성을 도와주는 프레임워크(xUnit)
- 개발환경(x)에 따라 Unit 테스트를 도와주는 도구
- JUnit - Java
- DBUnit - DB
- CppUnit - C++
- NUnit - .net
4. Hello Controller 테스트 코드 작성
- 프로젝트 패키지 및 메인 클래스 생성
- @SpringBootApplication : 스프링 부트의 자동 설정, 스프링 Bean 읽기/생성을 모두 자동으로 설정됨
@SpringBootApplication이 있는 위치부터 설정을 읽어가기 때문에 이 클래스는 항상 프로젝트의 최상단에 위치해야 함
- main 메소드에서 실행하는 SpringApplication.run으로 인해 내장 WAS(Web Application Server)를 실행
- 내장 WAS : 별도로 외부에 WAS를 두지 않고 애플리케이션을 실행할 때 내부에서 WAS를 실행하며,
항상 서버에 톰캣을 설치할 필요가 없게 되고, 스프링 부트로 만들어진 Jar 파일로 실행하면 됨
- 내장 WAS의 장점은 언제 어디서나 같은 환경에서 스프링 부트를 배포할 수 있음
//Application.java
package com.jojoldu.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);
}
}
- 테스트를 위한 Controller 생성
- Controller와 관련된 클래스들은 web 패키지에 담음
- @RestController : 컨트롤러를 JSON을 반환하는 컨트롤러로 만들어줌
이전 @ResponseBody를 각 메소드마다 선언했던 것을 한번에 사용할 수 있게 만들었음
- @GetMapping : HTTP Method인 Get의 요청을 받을 수 있는 API를 만들어줌
이전 @RequestMapping(method = RequestMethod.GET)으로 사용되었지만,
현재 프로젝트는 /hello로 요청이 오면 문자열 hello를 반환하는 기능을 가지게 되었음
//HelloController.java
package com.jojoldu.book.springboot.web;
import com.jojoldu.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 // 1
public class HelloController {
@GetMapping("/hello") // 2
public String hello(){
return "hello";
}
}
- 테스트 코드를 작성할 클래스 생성
- @RunWith(SpringRunner.class) : 테스트를 진행할 때, JUnit에 내장된 실행자 외에 다른 실행자를 실행시킴
SpringRunner라는 스프링 실행자를 사용하며,
스프링 부트 테스트와 JUnit 사이에 연결자 역할을 함
- @WebMvcTest : 스프링 테스트 어노테이션 중, Web(Spring MVC)에 집중할 수 있는 어노테이션으로,
선언할 경우, @Controller, @ControllerAdvice 등을 사용할 수 있지만,
@Service, @Component, @Repository 등은 사용할 수 없음
- @Autowired : 스프링이 관리하는 Bean을 주입 받음
- private MockMvc mvc : 웹 API를 테스트할 때 사용하며, 스프링 MVC 테스트의 시작점이고,
이 클래스를 통해 HTTP GET, POST 등에 대한 API 테스트를 할 수 있음
- mvc.perform(get("/hello")) : MockMvc를 통해 /hello 주소로 HTTP GET 요청을 하며,
체이닝이 지원되어 여러 검증 기능을 이어서 선언할 수 있음
메소드 체이닝이란 여러 메소드를 이어서 호출하는 문법이며,
메소드가 객체(this)를 반환하여 여러 메소드를 순차적으로 선언할 수 있도록 함
- .andExpect(status().isOk()) : mvc.perform의 결과를 검증하고, HTTP Header의 Status를 검증함
- .andExpect(content().string(hello)) : mvc.perform의 결과를 검증하고, 응답 본문의 내용을 검증함
Controller에서 "hello"를 리턴하기 때문에 이 값이 맞는지 검증함
//HelloControllerTest.java
package com.jojoldu.book.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.*;
import static org.hamcrest.Matchers.is;
@RunWith(SpringRunner.class) // 1
@WebMvcTest(controllers = HelloController.class) // 2
public class HelloControllerTest {
@Autowired // 3
private MockMvc mvc; // 4
@Test
public void hello가_리턴된다() throws Exception{
String hello = "hello";
// 5 // 6 // 7
mvc.perform(get("/hello")).andExpect(status().isOk()).andExpect(content().string(hello));
}
}
[테스트 메소드 실행]
[main 메소드 실행]
==> 테스트 메소드 실행 때와 마찬가지로 스프링 부트 로그가 보이며, 톰캣 서버 8080 포트로 실행되었다는 것도 로그에 출력됨
- 자바 개발자들의 필수 라이브러리 롬복
- 롬복 설정을 위해 'Enable annotation processing' 체크
- build.gradle
//build.gradle
buildscript {
ext{ //build.gradle에서 사용하는 전역변수를 설정하겠다는 의미
springBootVersion = '2.1.7.RELEASE' //spring-boot-gradle-plugin라는
// 스프링부트그레이들 플러그인의 2.1.7RELEASE를 의존성으로 받겠다.
}
repositories { //각종 의존성(라이브러리)들을 어떤 원격 저장소에서 받을지 정한다.
mavenCentral()
jcenter()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin : 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management' //스프링부트의 의존성을 관리해주는 플러그인!!!중요!!
group 'com.book'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-web')
testImplementation('org.springframework.boot:spring-boot-starter-test')
// 롬복 버전 5.x 이상임으로 아래 롬복 의존성 라이브러리를 추가함
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// 롬복 버전 5.x 미만은 아래 롬복 의존성 라이브러리를 사용함
// implementation 'org.projectlombok:lombok'
// Gradle을 통해서 롬복 의존성 라이브러리를 추가해주었지만 동작하지 않는다면
// 자신의 Gradle 버전이 5.x 이상인지 확인해주셔야합니다.
// 그 이유는 Gradle 버전이 올라가면서 Lombok 의존성을 추가하는 방법이 바뀌었기 때문입니다.
}
test {
useJUnitPlatform()
}
- HelloController 코드를 롬복으로 전환
- 모든 응답 Dto는 이 Dto 패키지에 추가
- @RequiredArgsConstructor : 선언된 모든 final 필드가 포함된 생성자를 생성해주며, final이 없는 필드는 생성자에 포함되지 않음
//HelloResponseDto.java
package com.jojoldu.book.springboot.web.dto;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter // 1
@RequiredArgsConstructor // 2
public class HelloResponseDto {
private final String name;
private final int amount;
}
- Dto에 적용된 롬복이 작동하는지 테스트 코드를 통해 확인
- assertThat : assertj라는 테스트 검증 라이브러리의 검증 메소드이며, 검증하고 싶은 대상을 메소드 인자로 받음
메소드 체이닝이 지원되어 isEqualTo와 같이 메소드를 이어서 사용할 수 있음
- isEqualTo : assertj의 동등 비교 메소드로, assertThat에 있는 값과 isEqualTo의 값을 비교해서 같을 때만 성공
- assertj의 장점 : CoreMatchers와 달리 추가적으로 라이브러리가 필요하지 않으며, 자동완성이 좀 더 확실하게 지원됨
<-> Junit의 assertThat을 쓰게 되면 is()와 같이 CoreMatchers 라이브러리가 필요하고,
IDE에서는 CoreMatchers와 같은 Matcher 라이브러리의 자동완성 지원이 약함
//HelloResponseDtoTest.java
package com.jojoldu.book.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);
}
}
[dto 테스트 메소드 결과]
==> 그레들 버전이 5이상인지 5미만인지 확인이 필요함
- HelloController에 ResponseDto 사용을 위한 코드 추가
- @RequestParam : 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션
외부에서 name(@RequestParam("name")이란 이름으로 넘긴
파라미터를 메소드 파라미터 name(String name)에 저장하게 됨
//HelloController.java
package com.jojoldu.book.springboot.web;
import com.jojoldu.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 // 1
public class HelloController {
@GetMapping("/hello") // 2
public String hello(){
return "hello";
}
@GetMapping("/hello/dto")
public HelloResponseDto helloDto(@RequestParam("name") String name, @RequestParam("amount") int amount){
return new HelloResponseDto(name, amount);
}
}
- 추가된 API를 테스트하는 코드를 HelloControllerTest에 추가
- param : API 테스트할 때 사용될 요청 파라미터를 설정함
단, 값은 String만 허용되며, 숫자/날짜 등의 데이터를 등록할 때는 문자열로 변경해야 함
- jsonPath : JSON 응답값을 필드별로 검증할 수 있는 메소드이며, $를 기준으로 필드명을 명시함
//HelloControllerTest.java
package com.jojoldu.book.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.*;
import static org.hamcrest.Matchers.is;
@RunWith(SpringRunner.class) // 1
@WebMvcTest(controllers = HelloController.class) // 2
public class HelloControllerTest {
@Autowired // 3
private MockMvc mvc; // 4
@Test
public void hello가_리턴된다() throws Exception{
String hello = "hello";
// 5 // 6 // 7
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") // 5
.param("name", name)
.param("amount",String.valueOf(amount))).andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(name))) // 6
.andExpect(jsonPath("$.amount", is(amount))); // 7
}
}
[dto API 테스트 결과]
'Web > SpringBoot' 카테고리의 다른 글
6. AWS 서버환경 - AWS EC2 (0) | 2022.03.04 |
---|---|
5. Spring security와 OAuth2.0으로 로그인 기능 구현 (0) | 2022.03.03 |
4. 머스테치로 화면 구성 (0) | 2022.02.28 |
3. SpringBoot에서 JPA로 데이터베이스 활용 (0) | 2022.02.27 |
1. IntelliJ 활용한 SpringBoot 시작하기 (0) | 2022.02.25 |
댓글