테스트코드를 작성하지 않는다면 오류가 발생하는 순간부터, 수정하고 반영하기 까지 계속 톰캣을 내렸다가 다시 실행하는 것을 반복한다.
테스트 코드를 작성하면, 이런 문제가 해결되는 것과 더불어 여러가지 장점이 있기 때문에 이번 프로젝트를 진행하면서 테스트 코드를 사용할 생각이다.
그럼 테스트코드를 작성하는 것의 여러가지 장점에 대해 살펴보자
코드의 신뢰성 향상
테스트 코드는 어떤 상황에서도 코드가 잘 동작하는지를 검증한다. 때문에, 코드의 버그를 미리 찾아내고 수정함으로써 코드의 신뢰성을 높일 수 있다.
리팩토링의 안전성 향상
코드를 리팩토링하면서 기능이 망가지지 않도록 하기 위해서는 테스트 코드가 필요하다. 리팩토링 과정에서 기능이 망가지지 않도록 코드를 안전하게 수정할 수 있다.
문서화 역할
테스트코드는 개발자들이 작성한 코드를 설명해주는 문서역할을 할 수 있다. 테스트 코드를 작성하면서 코드가 어떻게 동작하는지에 대한 이해를 높일 수 있기 때문이다.
다양한 테스트코드 프레임워크가 있지만, 그중 자바용인 JUnit을 사용할 계획이다.
우선 간단한 API를 만들자.
package com.myshop.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyShopController {
@GetMapping("/shop")
public String myShop() {
return "myShop";
}
}
위에 쓰여진 코드를 잠깐 설명해보자면
@RestController
: 스프링 프레임워크에서 제공하는 어노테이션 중 하나로, 웹 애플리케이션에서 Restful
웹 서비스를 개발할 때 사용한다. 이 어노테이션을 사용하면 해당 컨트롤러 클래스가 RESTful 웹서비스를 처리하는 컨트롤러임을 지정할 수 있다.
@GetMapping
: HTTP GET 요청을 처리하기 위한 메서드를 지정하는 어노테이션이다.
이제 작성한 코드가 제대로 작동하는지 테스트를 해보자.
src 폴더 밑에는 main과 test 디렉토리가 존재하고있는데, 테스트 코드를 작성하기 위해 test 디렉토리에 main디렉토리에서 생성했던 패키지를 그대로 다시 생성하면 된다.
콘트롤러 이름은 기존 컨트롤러 이름 뒤에 Test를 붙여서 테스트용 컨트롤러임을 명시한다.
package myshop.web;
import com.myshop.web.MyShopController;
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.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = MyShopController.class)
public class MyShopControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void hello가_리턴된다() throws Exception {
String myShop = "myShop";
mvc.perform(get("/shop"))
.andExpect(status().isOk())
.andExpect(content().string(myShop));
}
}
@RunWith(SpringRunner.class)
JUnit을 확장하여 스프링 테스트를 수행하는 방법을 지정하는 어노테이션이다.
테스트클래스 실행시 실행자(runner)
로 사용되는데, 스프링 컨텍스트를 로드하고 테스트에서 사용할 빈(bean)을 생성 및 주입(inject)하는 등의 스프링 테스트 환경을 설정한다. 위의 코드는 SpringRunner라는 스프링 실행자를 사용하여 스프링 부트 테스트와 JUnit 사이에 연결자 역할을 한다.
@WebMvcTest(controllers = MyShopController.class)
여러 스프링 테스트 어노테이션 중, Web(MVC)에 집중할 수 있는 어노테이션이다. 테스트할 컨트롤러만 로드되므로 더 빠르게 테스트를 수행할수 있다. @WebMvcTest 의 controllers
속성은 테스트할 컨트롤러 클래스를 작성한다.
@Autowired
private MockMvc mvc;
MockMvc는 스프링 MVC테스트에서 사용되는 클래스로, 스프링 애플리케이션의 HTTP 요청 및 응답을 테스트하기 위한 모의 객체를 생성한다. @Autowired
어노테이션을 통해 의존성을 주입하여 사용할 수 있다.
요약하면 위 코드는 HTTP GET, POST 등에 대한 API 테스트를 할 수 있도록 @AutoWired 어노테이션을 통해 의존성을 주입한 것이다.
mvc.perform(get("/shop"))
.andExpect(status().isOk())
.andExpect(content().string(myshop));
위에서 보이는 perform, andExpect는 Spring MVC Test 프레임워크에서 제공하는 메소드들인데, 좀 더 자세히 알아보도록 하자.
mvc.perform(get("/shop"))
MockMvc 객체의 perform()
메소드를 사용하여 "/shop" 주소로 HTTP GET 요청을 보낸다.
andExpect(status().isOk())
andExpect()
메소드를 사용하여 응답 상태코드가 200 OK인지 검증한다.
즉, HTTP 요청이 성공적으로 처리되어 200 상태코드를 반환하는지를 검증하는데, Boolean 값을 반환하며 상태코드가 200인 경우에만 테스트가 통과된다.
andExpect(content().string(hello))
HTTP 응답의 본문(content)을 검증하는 역할을 한다.
'string(hello)' 는 본문의 내용이 'hello' 문자열과 일치하는지를 검증한다. Boolean값을 반환하며 본문이 'hello' 문자열과 일치하는 경우에만 테스트가 통과된다.
무사히 실행이 완료되면 Tests passed에 초록색 체크아이콘이 생긴다.
참고 : 스프링 부트와 AWS로 혼자 구현하는 웹 서비스