[스프링부트와 AWS로 혼자 구현하는 웹 서비스] 따라하기 - 4

THOVY·2022년 6월 22일
0

따라하기

목록 보기
4/20

스프링부트와 AWS로 혼자 구현하는 웹 서비스 따라하기

시작👊

개요 - 스프링부트에서 테스트 코드를 작성하자!

요즘 거의 모든 회사들이 테스트 코드 경험을 요구한다고 한다.

( 그래서 ) 이번 시간에는 앞으로 진행할 프로젝트에서 가장 중요한 테스트 코드 작성의 기본을 배워봅니다.

빠밤

TDD 와 단위 테스트 는 다른 것이다

TDD 는 테스트가 주도하는 개발

  • 항상 실패하는 테스트를 먼저 작성하고(RED)
  • 테스트 통과하는 프로덕션 코드를 작성(GREEN)
  • 테스트 통과하면 프로덕션 코드를 리팩토링(REFACTOR)

단위 테스트는 TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것 (순수하게 테스트코드만 작성하는 것)

이번 장에서는 TDD 가 아닌 단위 테스트 코드를 배웁니다. 이번 장을 통헤 테스트 코드를 먼저 배운 뒤, TDD 를 배워보길 추천합니다.

넵!

참고: TDD 실천법과 도구

왜 테스트 코드를 작성해야 할까요?

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

무슨 말인지 모르겠지만 저자의 경험담을 읽어보니 이해가 됐다.

  1. 코드를 작성하고
  2. 프로그램을 실행한 뒤
  3. Postman 과 같은 API 테스트 도구로 HTTP 요청을 하고
  4. 요청 결과를 System.out.println() 으로 검증한다.
  5. 결과가 다르면 프로그램(tomcat)을 중지하고 코드를 수정합니다.
  6. 2~5를 반복 합니다.

톰캣을 재실행하는 시간은 수십초에서 1분 이상 소요되기도 하며 수십번씩 수정해야하는 상황에서 아무런 코드 작업 없이 1시간 이상 소요되기도 합니다.

완전 공감.

왜 계속 톰캣을 내렸다 실행하는 일을 반복할까요?

저자는 테스트 코드가 없어서 눈과 손으로 직접 수정된 기능을 확인할 수밖에 없기 때문이라고 말한다.

그럼 테스트 코드를 작성하면 이런 불필요한 반복확인을 하지 않아도 코드가 작동하는지 알 수 있다는 건가?

세 번째로 개발자가 만든 기능을 안전하게 보호해줍니다.
B 기능을 추가했더니 기존에 잘 작동되던 A기능에 문제가 생긴 것이 발견됩니다. 규모가 큰 서비스에서는 빈번하게 발생하며 하나의 기능을 추가할 때마다 너무나 많은 자원이 들기 때문에 서비스의 모든 기능을 테스트할 수는 없습니다.
이 때, 기존 기능이 잘 작동되는 것을 보장해주는 것이 테스트 코드입니다.

오? 어떻게 보장하지?
너무 흥미롭다.

테스트 코드 작성을 도와주는 프레임 워크

  • xUnit ( 저자가 가장 대중적이라고 )
    - JUnit(java), DBUnit(DB), CppUnit(C++) ..

우리는 자바 개발 환경이므로 JUnit 을 사용한다고 한다.


테스트코드 작성하기

  1. 자바 디렉토리에서 패키지 하나를 생성한다.

    ...?

    java 디렉토리가 어디있어..?




    하..
    다시

Java 디렉토리 만들기

gradle 프로젝트를 생성했는데 src 가 없다? java 디렉토리가 없다?

먼저 만들어야 됨

  1. 전체 선택 후 ENTER 치면 끝


다시 시작

테스트코드 작성하기

  1. 자바 디렉토리에 패키지를 생성한다.

  2. 패키지 이름은 일반적으로 웹사이트 주소의 역순으로 짓는다.
    2-1. 왜인지는 모르겠다.

  3. 그래서 우리는 com.prac.webservice 라고 지을 거다.

  4. 책처럼 뒤에 springboot도 추가하자

  5. 거기에 Application 이라는 이름의 java 클래스도 하나 만들자

  6. 클래스를 채워넣자

// 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 패키지 파일) 만 있으면 된다.(저자가 `후반부에서 톰캣없이 어떻게 배포하고 서비스 할 수 있는지 설명하니 조금만 기다려주세요!' 라고 하셨다. 기다린다)

컨트롤러를 만들어보자

  1. 지금 패키지 하위에 web 이란 패키지를 하나 더 만든다.
  2. 그 안에 HelloController 라는 자바 클래스를 만든다.
  3. 코드를 넣어준다
// 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 를 실행하지 않고 테스트 코드로 검증할거다!
두둥 긴장된다 나는 처음이니까.

  1. test 패키지에 우리가 java 패키지에 만들었던 패키지를 똑같이 만들어준다..

흐음

  1. 근데 이제 Appication 은 해당되지 않는다. 말로 말그대로 테스트할 코드만 넣어줄 거기때문에 web 으로 HelloController 를 만들어주면 된다.
    근데 이때 테스트 클래스의 이름은 원본 클래스 이름 뒤에 test 를 붙인다. 그래서 HelloControllerTest 로 만들어주자
  2. 코드를 채워준다.
// 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 에서 리턴값이 맞나 확인.

  1. 이제 test 파일에서 화살표를 눌러 실행해본다.
    ...?

이제 화낼 힘도 없어...

포기하지 않고 해결해보자

테스트 실행 을 인텔리제이로 바꿔본다.( 기본은 gradle 이다. )

어휴... 그만할래 오늘은..

profile
BEAT A SHOTGUN

0개의 댓글