요즘 백엔드 개발자/엔지니어 기업 공고를 보면 AWS, Docker, Elastic, RESTful 등등을 경험한 사람들을 우대사항으로 많이 뽑고 있습니다. 그 중
이런 우대사항을 많이 보셨을 겁니다. 이처럼 현재 테스트 코드를 작성하는 것이 필수적인 요소라고 해도 과언이 아닐 정도로 중요한 위치에 자리잡고 있습니다.
TDD란?
Test-Driven-Development, 말 그대로 테스트 주도 개발입니다. 보통의 경우 테스트를 마지막에 하는 것이 일반적인데, 테스트 케이스를 작성하고 이를 통과하는 코드를 짜서 개발을 합니다. 코드 구현을 하려면 테스트 케이스를 작성해야 하기 때문에 시간이 오래걸리지만, 이것이 반복되면서 버그도 줄어들고 요구사항 변동에도 유연하게 대처할 수 있습니다.
단위테스트란?
Unit Test라고 불리며, 구현한 모듈이나 어플리케이션 안의 개별적인 코드가 정상적으로 작동되는지 자동반복되는 테스트를 의미합니다. 다른 테스트들에 비해 가장 작은 단위의 테스트입니다. 다음과 같은 장점을 가지고 있습니다.
- 개발 초기에 문제를 발견할 수 있다.
- 코드 결과를 보기위해 서버를 닫았다가 다시 열 필요가 없다.
- A 모듈의 테스트 코드가 B 모듈의 기능이 잘 작동되는 것을 보장한다.
테스트 코드를 작성하면 이와같은 효과를 기대할 수 있지만, 그만큼 단위 테스트를 잘 설계하는 것이 중요하겠죠?
main-java 에 패키지를 만듭니다. 보통 패키지명은 웹사이트 주소의 역순으로 만듭니다.
이렇게 Application 이라는 java class를 패키지 안에 만들었습니다.
package com.qweadzs;
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 읽기와 생성을 자동으로 설정 됩니다. @SBA이 있는 위치부터 설정을 읽기 때문에 항상 최상단에 위치해야합니다.
main에 run으로 인해 내장 WAS를 실행합니다. 내장 WAS는 서버에 톰캣을 설치할 필요가 없어지게되고 스프링 부트로 만들어진 Jar파일로 실행할 수 있게 해줍니다. 내장 WAS의 사용은 언제 어디서나 같은 환경에서 스프링을 배포할 수 있게 해주므로 유용하게 사용됩니다.
현재 패키지 하위에 web이라는 컨트롤러 관련 클래스들을 담는 패키지를 만들었습니다.
HelloController라는 클래스를 만들고 API를 작성하겠습니다.
package com.qweadzs.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // --1.
public class HelloController {
@GetMapping("/hello") // --2.
public String hello(){
return "hello";
}
}
이제 테스트 코드로 검증해 보겠습니다. test/java에 패키지를 아까와 같이 만듭니다. 일반적으로 테스트 클래스는 클래스이름 뒤에 Test를 붙입니다.
package qweadzs.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 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;
@RunWith(SpringRunner.class) // 1.
@WebMvcTest(controllers = HelloController.class) // 2.
public class HelloController {
@Autowired // 3.
private MockMvc mvc; // 4.
@Test
public void hello가_리턴된다() throws Exception{
String hello = "hello";
mvc.perform(get("/hello")) // 5.
.andExpect(status().isOk())// 6.
.andExpect(content().string(hello)); // 7.
}
}
@RunWith(SpringRunner.class)
@WebMvcTest
@Autowired
private MockMvc mvc
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello))
우리는 인텔리제이에서 그래들로 프로젝트를 만들었기 때문에 테스트와 빌드 자체가 그래들로 기본값 설정이 되어있습니다. 이것을 인텔리제이로 바꾸어야 테스트시 오류가 나지 않습니다.
Preferences -> Build, Excutino, Deployment -> Build Tools -> Gradle ->
이제 Application.main을 실행하면 톰캣서버가 8080포트로 실행되었다는 로그와 함께 로컬호스트로 접속이 가능합니다. http://localhost:8080/hello 접속!
LOMBOK이란?
LOMBOK(롬복)은 자바 개발시 거의 필수적인 라이브러리라고 할 수 있습니다. DTO 작성시 toString, get, set 등을 생산자 혹은 메소드로 많이 쓰고 있지만 실제로 이렇게 쓰다 보면 코드가 정말 길어지고 복잡해지기 마련입니다. 롬복은 이러한 작업들을 클래스 앞에 어노테이션만 쓰면 컴파일시 자동으로 생성해주어 코드의 가독성을 높이고 생산성을 높여줍니다.
주의할 점은 플러그인은 한번만 설치하면 되지만 세팅은 프로젝트마다 해야합니다.
main/java/groupid/web 패키지에 dto 패키지를 추가하고, dto패키지 안에 HelloResponseDto를 작성하겠습니다.
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter // 1.
@RequiredArgsConstructor // 2.
public class HelloResponseDto {
private final String name;
private final int amount;
}
이 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);// 1, 2
assertThat(dto.getAmount()).isEqualTo(amount);
}
}
보시는 바와 같이 getName이나 getAmount라는 메소드와 생성자의 선언 없이 어노테이션만으로 자동으로 생성되는 것을 확인했습니다.
다음은 HelloController에 방금만든 Dto를 적용하겠습니다.
@GetMapping("/hello/dto")
public HelloResponseDto helloDto(@RequestParam("name") String name, // 1.
@RequestParam("amount") int amount) {
return new HelloResponseDto(name, amount);
}
name, amount는 API를 호출하는 곳에서 넘겨준 값들입니다. 추가된 API를 테스트하는 코드를 HelloControllerTest에 추가합니다.
Tip. Ctrl + E를 누르면 최근 열었던 파일을 볼 수 있습니다. 파일이 점점 많아질때 유용하게 사용할 수 있습니다.
@Test
public void helloDto가_리턴된다() throws Exception {
String name = "hello";
int amount = 1000;
mvc.perform(
get("/hello/dto")
.param("name", name) // 1.
.param("amount", String.valueOf(amount)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(name))) // 2.
.andExpect(jsonPath("$.amount", is(amount)));
}