Hi yoahn 개발블로그

#2 스프링부트 테스트코드 본문

Framework & Library/springboot

#2 스프링부트 테스트코드

hi._.0seon 2020. 8. 31. 15:34
반응형

TDD (Test Driven Development)

테스트가 주도하는 개발

테스트 코드를 먼저 작성하는 것부터 시작

 

<레드 그린 사이클>

- 항상 실패하는 테스트를 먼저 작성하고 Red

- 테스트가 통과하는 프로덕션 코드를 작성하고 Green

- 테스트가 통과하면 프로덕션 코드를 리팩토링 Refactor

 

단위 테스트

TDD의 첫번째 단계인 기능 단위의 테스트 코드를 작성하는 것

TDD와 달리 테스트 코드를 꼭 먼저 작성하지 않아도 되고, 리팩토링도 포함되지 않는다

 

테스트코드를 작성하는 이유

1. 테스트 코드를 작성하면 수정된 기능을 확인하기 위해 프로그램을 매번 재시작하지 않아도 된다

2. 작성된 단위 테스트를 실행만 하면 수동검증은 필요없게 된다.

(System.out.println 으로 매번 눈으로 확인하는 문제가 없어짐)

3. 개발자가 만든 기능을 안전하게 보호해준다.

(새로운 기능이 추가되어 기존 기능이 작동되지 않는 것을 조기에 발견할 수 있다)

 

1.1. 테스트 코드 작성하기

프로젝트 디렉토리에서 패키지를 생성

src/main/java/

밑에 패키지를 생성하고 생성한 패키지 아래에 Java 클래스를 생성한다.

 

Application.java

package <패키지명>;

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이 있는 위치부터 설정을 읽어가기 때문에 이 클래스는 항상 프로젝트의 최상단에 위치해야 한다.

 

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

(내장 WAS: 별도로 외부에 WAS를 두지 않고 애플리케이션 실행 시 내부에서 WAS를 실행하는 것)

-> 이렇게 하면 항상 서버에 톰캣을 설치할 필요가 없게 되고, 스프링 부트로 만들어진 Jar 파일로 실행하면 된다.

 

* 스프링 부트에서는 내장 WAS를 사용하는 것을 권장하고 있다 (p.58)

-> 언제 어디서나 같은 환경에서 스프링 부트를 배포할 수 있기 때문에

 

Bean 이란?

gmlwjd9405.github.io/2018/11/10/spring-beans.html

 

[Spring] Spring Bean의 개념과 Bean Scope 종류 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

1.2. 테스트를 위한 컨트롤러 생성하기

현재 패키지 하위에 web이란 패키지 생성

# 컨트롤러와 관련된 클래스들은 모두 이 패키지에 담음

HelloController.java

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을 반환하는 컨트롤러로 만들어 준다

 

@GetMapping

HTTP 메소드인 Get의 요청을 받을 수 있는 API를 만들어 준다

 

-> 이 프로젝트는 /hello로 요청이 오면 문자열 hello를 반환하는 기능을 가지게 됨

 

1.3. 테스트 코드로 검증하기

import com.youngs.springboot.web.HelloController;
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 = 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 (Spring MVC)에 집중할 수 있는 어노테이션

- 선언할 경우 @Controller, @ControllerAdvice 등을 사용할 수 있다

- 단, @Service, @Component, @Repository 등은 사용할 수 없다

- 여기서는 컨트롤러만 사용하기 때문에 선언

 

@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, 404, 500 등의 상태를 검증

- 여기선 OK == 200 인지 아닌지를 검증

 

# .andExpect(content().string(hello))

- mvc.perform의 결과를 검증

- 응답 본문의 내용 검증

- Controller에서 "hello"를 리턴하기 때문에 이 값이 맞는지 검증한다.

 

2.1. 롬복 소개 및 설치하기   p.67

롬복

자바 개발 시 자주 사용하는 코드 Getter, Setter, 기본 생성자, toString 등을 어노테이션으로 자동 생성해준다

 

롬복은 프로젝트마다 설정해야 한다.

플러그인 설치는 한번만 하면 되지만, build.gradle에 라이브러리를 추가하는 것과

   Preferences>Build>Compiler>Annotation Processors>Enable annotation processing 체크는 프로젝트마다 진행해야 함

2.2 기존 코드 롬복으로 리팩토링 하기  p.71

web 패키지> dto 패키지 추가

=> 모든 응답 Dto는 이 Dto 패키지에 추가

 

...springboot.web.dto => HelloResponseDto.java

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class HelloResponseDto {

    private final String name;
    private final int amount;
}

@Getter

선언된 모든 필드의 get 메소드를 생성해 준다

 

@RequiredArgsConstructor

선언된 모든 final 필드가 포함된 생성자를 생성해 준다

final 이 없는 필드는 생성자에 포함되지 않는다

 

이에 대한 테스트 코드 작성

test.java....springboot.web.dto => HelloResponseDtoTest.java

import com.youngs.springboot.web.dto.HelloResponseDto;
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);
        assertThat(dto.getAmount()).isEqualTo(amount);
    }
}

# assertThat

- assertj 라는 테스트 검증 라이브러리의 검증 메소드

- 검증하고 싶은 대상을 메소드 인자로 받음

- 메소드 체이닝이 지원됨.

 

# isEqualTo

- assertj 의 동등 비교 메소드 

- assertThat 에 있는 값과 isEqualTo의 값을 비교해서 같을 때만 성공

 

 

HelloController에도 새로 만든 ResponseDto를 사용하도록 코드를 추가

HelloController.java

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

    @GetMapping("/hello/dto")
    public HelloResponseDto helloDto(@RequestParam("name") String name,
                                     @RequestParam("amount") int amount) {
        return new HelloResponseDto(name, amount);
    }
}

@RequestParam

- 외부에서 API 로 넘긴 파라미터를 가져오는 어노테이션

- 외부에서 name (@RequestParam("name")) 이란 이름으로 넘긴 파라미터를 메소드 파라미터 name (String name)에 저장하게 됨

 

추가된 API를 테스트하는 코드를 HelloControllerTest에 추가하기

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.*;

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

    @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만 허용된다

- 그래서 숫자/날짜 등의 데이터도 등록할 때는 문자열로 변경해야만 가능

 

# jsonPath()

- JSON 응답값을 필드별로 검증할 수 있는 메소드

- $ 를 기준으로 필드명을 명시함

- name, amount를 검증 -> $.name, $.amount 로 검증

 

 

반응형
Comments