테스트코드 작성에 대한 학습(TDD가 아닌, 단위테스트)
단위 테스트 코드는 하기와 같은 이점이 있다
즉 테스트 코드는
TDD에 대해서는 TDD 실천법과 도구 참고
JUnit을 사용하여 테스트 코드 작성
Lombok 설치 및 기존 프로젝트를 Lombok 코드로 전환
// 프로젝트 빌드를 위해 필요한 플러그인들을 지정해주는 블록
// 플러그인들은 필요한 과정들을 task로 포함하고 있고, 빌드시에는 필요한 과정들을 내부 task가 진행해줌.
plugins{
id 'org.springframework.boot' version '2.4.1'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group 'com.jojoldu.book'
version '1.0.4-SNAPSHOT-'+new Date().format("yyyyMMddHHmmss")
// 각종 의존성(라이브러리)들을 어떤 원격 저장소에서 받을지를 결정
// 기존적으로 mavenCentral을 많이 사용하지만, 라이브러리 업로드 난이도 때문에 jcenter도 많이 사용
repositories {
mavenCentral()
jcenter()
}
test {
useJUnitPlatform()
}
// 의존성에 대한 설정들을 관리하는 프로퍼티로 필요한 라이브러리 등의 정보를 기술하여 참조
// implementation: 컴파일 할 때 접근, testImplementation: 테스트를 컴파일할 때 접근
// compileOnly: 컴파일시에만 사용, runtimeOnly: 런타임시에만 사용
dependencies {
// spring boot
implementation('org.springframework.boot:spring-boot-starter-web')
testImplementation('org.springframework.boot:spring-boot-starter-test')
implementation('org.springframework.boot:spring-boot-starter-mustache')
//lombok
compileOnly 'org.projectlombok:lombok:1.18.20'
annotationProcessor 'org.projectlombok:lombok:1.18.20'
testCompileOnly 'org.projectlombok:lombok:1.18.20'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.20'
}
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);
}
}
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 // 컨트롤러를 JSON을 반환하는 컨트롤러로 만들어 줌
public class HelloController
{
@GetMapping("/hello") // HTTP Method인 Get의 요청을 받을 수 있는 API로 만들어 줌
public String hello()
{
return "hello";
}
@GetMapping("/hello/dto")
// @RequestParam은 외부에서 API로 파라미터를 가져오는 어노테이션
// 외부에서 "name"이란 이름으로 넘긴 파라미터를 메소드 파라미터 name에 저장
public HelloResponseDto helloDto(@RequestParam("name") String name, @RequestParam("amount") int amount)
{
return new HelloResponseDto(name, amount);
}
}
package com.jojoldu.book.springboot.web;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
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;
// 테스트를 진행할 때 JUnit에 내장된 실행자 외에 다른 실행자를 실행
// 스프링 부트 테스트와 JUnit 사이에 연결자 역할
@ExtendWith(SpringExtension.class)
// 여러 스프링 테스트 어노테이션 중, Web(Spring MVC)에 집중할 수 있는 어노테이션으로 아래 내용만 스캔함
// @Controller, @ControllerAdive, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest
{
@Autowired // 스프링이 관리하는 빈(Bean)을 주입 받음
private MockMvc mvc; // 웹 API를 테스트할 때 사용하는 클래스로 HTTP GET, POST 등의 API 테스트 가능
@Test // 테스트 코드 표시
public void returnHello() throws Exception
{
String hello = "hello";
mvc.perform(get("/hello")) // MockMVC를 통해 /hello 주소로 HTTP GET 요청
.andExpect(status().isOk()) // mvc.perform의 결과를 검증하는데, HTTP Header의 Status를 검증하는 역할
.andExpect(content().string(hello)); // 응답 본문의 내용을 검증하는 역할(이 경우에는 hello가 리턴 되는지)
}
@Test
public void returnHelloDto() throws Exception
{
String name = "hello";
int amount = 1000;
mvc.perform(
get("/hello/dto")
.param("name", name) // API를 테스트할 때 사용될 요청 파라미터를 설정
.param("amount", String.valueOf(amount)) // 값은 무조건 String으로 다른 타입일 시 타입 변환해 사용
).andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(name))) // jsonpath는 응답값을 필드별로 검증할 수 있는 메소드
.andExpect(jsonPath("$.amount", is(amount))); // $를 기준으로 필드명을 명시
}
}
package com.jojoldu.book.springboot.web.dto;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class HelloResponseDtoTest
{
@Test
public void lombokTest()
{
String name = "test";
int amount = 1000;
HelloResponseDto dto = new HelloResponseDto(name, amount);
// assertj라는 테스트 검증 라이브러리의 검증 메소드로 검증하고 싶은 대상을 메소드 인자로 받음
// assertThat에 있는 값이 isEqaulTo의 값과 같을 때만 성공
assertThat(dto.getName()).isEqualTo(name);
assertThat(dto.getAmount()).isEqualTo(amount);
}
}