스프링 부트와 AWS로 혼자 구현하는 웹 서비스[2]

uuuu.jini·2022년 3월 22일
0
post-thumbnail

🐯 02장 . 스프링 부트에서 테스트코드를 작성하자 🐯

  1. 테스트 코드 소개
  2. Hello Controller 테스트 코드 작성
  3. 롬복 소개 및 설치
  4. Hello Controler코드를 롬복으로 전환

최근의 추세는 대부분의 서비스 회사가 테스트 코드에 관해 요구하고 있다. 2장에서는 프로젝트에서 가장 중요한 테스트 코드 작성의 기본을 배워본다.

1. 테스트 코드 소개

TDD와 단위 테스튼는 다른 이야기이다. TDD는 테스트가 주도하는 개발을 이야기하며, 테스트 코드를 먼저 작성하는 것부터 시작한다. 반면 단위테스트는 TDD의 첫번째 단계인 기능 단위의 테스트 코드를 작성 하는 것을 이야기 한다.

단위 테스트 코드 작성 이유

  • 단위 테스트는 개발 단계 초기에 문제를 발견하게 도와준다.
  • 단위 테스트는 개발자가 나중에 코드를 리팩토링하거나 라이브러리 업그레이드 등에서 기존 기능이 올바르게 작동하는지 확인 할 수 있다.
  • 단위 테스트는 기능에 대한 불확실성을 감소시킬 수 있다.
  • 단위 테스트는 시스템에 대한 실제 문서를 제공한다. ( 단위 테스트 자체가 문서로 사용할 수 있다. )

필자의 경험담

  • 빠른 피드백 ( 톰캣을 내렸다가 다시 실행하는 일의 반복을 줄인다. )
  • 사람이 눈으로 검증하지 않게 자동 검증 가능
  • 개발자가 만든 기능을 안전하게 보호

가장 대중적인 테스트 프레임워크로는 xUnit이 있다. 이는 개발환경(x)에 따라 unit테스트를 도와주는 도구이다.

  • Junit - java
  • DBUnit - DB
  • CppUnit - C++
  • NUnit - .net

이 중 자바용인 JUnit을 사용한다. (특히, JUnit4를 사용)


2. Hello Controller 테스트 코드 작성하기

만든 프로젝트에서 패키지를 생성한다. 일반적으로 패키지명은 웹사이트 주소의 역순 으로 한다. (예 - admin.jojoldu.com --> com.jojoldu.admin )

Application 클래스 작성

생성된 패키지에 자바 클래스를 Application이라는 이름으로 생성한 후 클래스의 코드를 아래와 같이 작성한다.

package com.jojoldu.book.springbootaws;

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);
    }
}

해당 Application 클래스는 앞으로 만들 프로젝트의 메인 클래스 이다. @SpringBootApplication 으로 인해 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성을 모두 자동으로 설정된다. 특히 @SpringBootApplication 이 있는 위치부터 설정을 읽어 가기 때문에 이 클래스는 항상 프로젝트의 최상단에 위치 해야 한다.

메인 메소드에서 실행하는 SpringApplication.run으로 인해 내장 WAS(웹어플리케이션 서버)를 실행한다.

내장 WAS란 별도로 외부에 WAS를 두지 않고 애플리케이션을 실행할 때에 내부에서 WAS를 실행하는 것을 이야기 한다. 이를 사용하면 항상 서버에 톰캣을 설치할 필요가 없게 되고 스프링 부트로 만들어진 Jar파일(실행가능한 java패키징 파일)로 실행하면 된다.

Controller 클래스 작성

현재 패키지 하위에 web패키지를 생성한다. 해당 패키지에 Controller관련 클래스들은 모두 담겠다. 컨트롤러 클래스를 생성한다.

package com.jojoldu.book.springbootaws.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";
    }

    
}

controller클래스의 내용이다.

  • @RestController : 컨트롤러를 JSON을 반환하는 컨트롤러로 만들어준다.
  • @GetMapping : Get요청을 받을 수 있는 API를 만들어 준다.

이제 이 프로젝트는 /hello요청이 오면 문자열 hello를 반환하는 기능을 가진다.

테스트 코드 작성

작성한 코드가 제대로 작동하는지 WAS를 실행하지 않고 테스트코드로 검증 한다. src/test/java 디렉토리에 앞에서 생성했던 패키지를 그대로 다시 생성한다. 그리고 테스트 코드를 작성할 클래스를 생성한다. 테스트 클래스 이름에 Test를 붙인다.

package com.jojoldu.book.springbootaws.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.boot.test.context.SpringBootTest;
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));
    }
    
}
  • RunWith(SpringRunner.class) : 테스트를 진행할 때 JUnit에 내장된 실행자 외에 다른 실행를 실행시킨다. SpringRunner라는 스프링 실행자를 사용한다. 즉, 스프링 부트 테스트와 JUnit 사이에 연결자 역할을 한다.

  • WebMvcTest : 여러 스프링 테스트 어노테이션 중, Web에 집중할 수 있는 어노테이션이다. 선언할 경우 @Controller,@ControllerAdvice등을 사용할 수 있으며 단, @Service,@Component,@Repository등은 사용할 수 없다. 여기서는 컨트롤러만 사용하기 때문에 선언한다.

  • @Autowired : 스프링이 관리하는 빈을 주입받는다.

  • 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,404,500등의 상태, Ok- 200)

  • .andExpect(content().string(hello)) : mvc.perform의 결과를 검증하며 응답 본문의 내용을 검증한다. Controller에서 "hello"를 리턴하기 때문에 이 값이 맞는지 검증한다.

위의 테스트를 실행한다.


3. 롬복 소개 및 설치하기

롬복은 자바 개발할 때 자주 사용하는 코드 Getter,Setter,기본 생성자,toString등을 어노테이션으로 자동 생성해 준다. 인텔리제이에서 플러그인 덕분에 쉽게 설정이 가능하다.

프로젝트에 롬복 추가

build.gradle에 다음의 코드를 추가한다.

compile('org.projectlombok:lombok')

Refresh로 새로고침해서 라이브러리를 내려받는다.

라이브러리를 다 받았다면 롬복 플러그인을 설치한다. Action을 검색하여 plugins에서 lombok을 설치한다.


4. Hello Controller 코드를 롬복으로 전환하기

먼저 web패키지에 dto패키지를 추가한다. 앞으로 모든 응답 Dto는 이 Dto패키지에 추가한다. 이 패키지에 HelloResponseDto를 생성한다.

package com.jojoldu.book.springbootaws.dto;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
    private final String name;
    private final int amount;
}
  • @Getter : 선언된 모든 필드의 get메소드를 생성
  • @RequiredArgsConstructor : 선언된 모든 final필드가 포함된 생성자를 생성. final이 없는 필드는 생성자에 포함되지 않음.

테스트 코드 작성

package com.jojoldu.book.springbootaws.web.dto;

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class HelloResponseDtoTest {

    @Test
    public void 롬복_기능_테스트(){
        String name = "test";
        int amount = 1000;

        //when
        HelloResponseDto helloResponseDto = new HelloResponseDto(name,amount);

        //then
        assertThat(helloResponseDto.getName()).isEqualTo(name);
        assertThat(helloResponseDto.getAmount()).isEqualTo(amount);


    }
}
  • asserThat : assertJ라는 테스트 검증 라이브러리의 검증 메소드이다. 검증하고 싶은 대상을 메서드 인자로 받으며 메소드 체이닝이 지원되어 isEqualTo와 같이 메서드를 이어서 사용한다.
  • isEqualTo : assertJ의 동등 비교 메소드이다. assertThat에 있는 값과 isEqualTo의 값을 비교해서 같을 때만 성공이다.

assertJ의 assertThat : Junit의 assertThat이 아닌 assertj의 장점은 CoreMatchers와 달리 추가 라이브러리가 필요하지 않으며 자동완성이 좀더 확실하게 지원된다.

테스트를 실행한다.

HelloController에 ResponseDto 사용

    @GetMapping("/hello/dto")
    public HelloResponseDto helloDto(@RequestParam("name") String name,@RequestParam("amount") int amount){
        return new HelloResponseDto(name,amount);
    }
  • @RequestParam : 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션이다. 외부에서 name이라는 이름으로 넘긴 파라미터를 메소드 파라미터에 저장한다.

name과 amount는 API를 호출하는 곳에서 넘겨준 값들이다.

테스트 하기

    @Test
    public void helloDto가_리턴된다() throws  Exception{
        String name = "hello";
        int amount = 1000;
        
        mvc.perform(
                get("/hello/dto")
                        .param("name",name)
                        .param("amount",String.valueOf(amount))
        )
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name",is(name)))
                .andExpect(jsonPath("$.amount",is(amount)));
    }
  • param : API 테스트할 때 사용될 요청 파라미터를 설정한다. 단 값은 String만 허용한다. 숫자/날짜 등의 데이터 등록위해 문자열로 변경해야 한다. ( String.valueOf(int) )

  • jsonPath : JSON 응답값을 필드별로 검증할 수 있는 메서드이다. $를 기준으로 필드명을 명시한다.

profile
멋쟁이 토마토

0개의 댓글