책 스프링부트와 AWS로 혼자 구현하는 웹 서비스
따라하기
( 그래서 ) 이번 시간에는 앞으로 진행할 프로젝트에서 가장 중요한 테스트 코드 작성의 기본을 배워봅니다.
빠밤
TDD 는 테스트가 주도하는 개발
단위 테스트는 TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것 (순수하게 테스트코드만 작성하는 것)
이번 장에서는 TDD 가 아닌 단위 테스트 코드를 배웁니다. 이번 장을 통헤 테스트 코드를 먼저 배운 뒤, TDD 를 배워보길 추천합니다.
넵!
참고: TDD 실천법과 도구
왜 테스트 코드를 작성해야 할까요?
- 개발단계 초기에 문제를 발견하게 도와줍니다.
- 나중에 코드를 리팩토링하거나 라이브러리 업그레이드 등에서기존 기능이 올바르게 작동하는지 확인할 수 있습니다.(회귀 테스트)
- 기능에 대한 불확실성을 감소시킬 수 있습니다.
- 시스템에 대한 실제 문서를 제공합니다. 즉, 단위 테스트 자체가 문서로서 사용될 수 있습니다.
무슨 말인지 모르겠지만 저자의 경험담을 읽어보니 이해가 됐다.
- 코드를 작성하고
- 프로그램을 실행한 뒤
- Postman 과 같은 API 테스트 도구로 HTTP 요청을 하고
- 요청 결과를 System.out.println() 으로 검증한다.
- 결과가 다르면 프로그램(tomcat)을 중지하고 코드를 수정합니다.
- 2~5를 반복 합니다.
톰캣을 재실행하는 시간은 수십초에서 1분 이상 소요되기도 하며 수십번씩 수정해야하는 상황에서 아무런 코드 작업 없이 1시간 이상 소요되기도 합니다.
완전 공감.
왜 계속 톰캣을 내렸다 실행하는 일을 반복할까요?
저자는 테스트 코드가 없어서 눈과 손으로 직접 수정된 기능을 확인할 수밖에 없기 때문이라고 말한다.
그럼 테스트 코드를 작성하면 이런 불필요한 반복확인을 하지 않아도 코드가 작동하는지 알 수 있다는 건가?
세 번째로 개발자가 만든 기능을 안전하게 보호해줍니다.
B 기능을 추가했더니 기존에 잘 작동되던 A기능에 문제가 생긴 것이 발견됩니다. 규모가 큰 서비스에서는 빈번하게 발생하며 하나의 기능을 추가할 때마다 너무나 많은 자원이 들기 때문에 서비스의 모든 기능을 테스트할 수는 없습니다.
이 때, 기존 기능이 잘 작동되는 것을 보장해주는 것이 테스트 코드입니다.
오? 어떻게 보장하지?
너무 흥미롭다.
테스트 코드 작성을 도와주는 프레임 워크
- xUnit ( 저자가 가장 대중적이라고 )
- JUnit(java), DBUnit(DB), CppUnit(C++) ..
우리는 자바 개발 환경이므로 JUnit 을 사용한다고 한다.
gradle 프로젝트를 생성했는데 src 가 없다? java 디렉토리가 없다?
먼저 만들어야 됨
자바 디렉토리에 패키지를 생성한다.
패키지 이름은 일반적으로 웹사이트 주소의 역순으로 짓는다.
2-1. 왜인지는 모르겠다.
그래서 우리는 com.prac.webservice 라고 지을 거다.
책처럼 뒤에 springboot도 추가하자
거기에 Application
이라는 이름의 java 클래스도 하나 만들자
클래스를 채워넣자
// Application.java
package com.prac.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);
}
}
@SpringBootApplication
이 스프링 부트의 자동설정, 스프링 Bean 읽기와 생성을 모두 자동으로 설정해준다. 그래서 이 친구가 있는 곳이 제일 먼저 읽혀야하기 때문에 이 클래스가 프로젝트 최상단에 있어야한다.
메인 메서드 안에
SpringApplication.run
이 내장 WAS(Web Application Server) 를 실행해 별도로 외부 WAS 가 필요하지 않게 된다. 그래서 톰캣 같은 게 필요없고 스프링 부트로 만들어진 Jar 파일 (실행가능한 java 패키지 파일) 만 있으면 된다.(저자가 `후반부에서 톰캣없이 어떻게 배포하고 서비스 할 수 있는지 설명하니 조금만 기다려주세요!' 라고 하셨다. 기다린다)
web
이란 패키지를 하나 더 만든다.HelloController
라는 자바 클래스를 만든다.// HelloController.java
package com.prac.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";
}
}
@RestController
: 컨트롤러를 JSON 을 반환하는 컨트롤러로 만들어 준다.
예전에는@ResponseBody
를 각 메소드 마다 선언했었는데 그거를 클래스 위에 한 번 적어주는 걸로 한 번에 해준다.
@GetMapping
: HTTP Method 인 Get 의 요청을 받을 수 있는 API 를 만들어준다. 예전에는@RequestMapping(method = RequestMethod.Get)
으로 사용되었단다.
WAS 를 실행하지 않고 테스트 코드로 검증할거다!
두둥 긴장된다 나는 처음이니까.
test
패키지에 우리가 java
패키지에 만들었던 패키지를 똑같이 만들어준다..흐음
Appication
은 해당되지 않는다. 말로 말그대로 테스트할 코드만 넣어줄 거기때문에 web 으로 HelloController
를 만들어주면 된다.HelloControllerTest
로 만들어주자// HelloControllerTest.java
package com.prac.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));
}
}
여기서 마지막 세 줄의 get
, status
, content
를 import 할 때 뭘 하는 지 잘 확인해야된다.
@RunWith(SpringRunner.class)
: 테스트를진행할 때 JUnit 에 내장된 실행자 외에 다른 실행자, 즉, SpringRunner 라는 스프링 실행자를 사용한다. 그래서 스프링 부트 테스트와 JUni 사이에 연결자 역할을 한단다.
@WebMvcTest1 : 여러 스프링 테스트 어노테이션 중, Web 에 집중할 수 있는 어노테이션이란다. 선언할 경우
@Controller@ControllerAdvice
등을 사용할 수 있으며
@Service@Component
@Repository` 등은 사용할 수 없다. 지금은 컨트롤러만 사용하기 때문에 선언한다.
...
나 Service Component Repository 만 사용해봤는데... ControllerAdvice 는 생전 첨듣는디...
너무 어렵수다
@Autowired
스프링이 관리하는 Bean 주입
private MockMvc mvc
: 웹 API를 테스트할 때 사용하는데, 스프링 MVC 테스트의 시작점이며, 이 클래스를 통해 HTTP GET, POST 에 대한 API 테스트를 할 수 있다.
mvc.perform(get("/hello"))
: MockMvc 를 통해 /hello 주소로 HTTP GET 요청을 하며, 체이닝이 지원되어 여러 검증 기능을 이어서 선언할 수 있다.
.andExpect(status().isOk())
: mvc.perform 의 결과를 검증하고 HTTP Header 의 Status 를 검증. 우리가 아는 그 200, 400, 500 같은 상태를 검증하는 것.
.andExpect(content().string(hello))
: mvc.perform 의 결과를검증하고, 본문의 내용을 검증한다. Controller 에서 리턴값이 맞나 확인.
이제 화낼 힘도 없어...
포기하지 않고 해결해보자
테스트 실행 을 인텔리제이로 바꿔본다.( 기본은 gradle 이다. )
어휴... 그만할래 오늘은..