스프링 부트에서의 테스트

김주언·2022년 10월 24일
0

Spring Boot

목록 보기
4/8
post-thumbnail

단위 테스트를 수행할 때는 테스트 대상이 되는 객체가 최대한 단순해야 효율적인 테스트가 가능하다. 그러나 일반적으로 웹 애플리케이션은 테스트 개상 객체가 특정 서버와 연관되어 있거나 다른 객체들과 연관되어 관계가 복잡한 경우가 많다.

이러한 경우 해당 객체를 단독으로 테스트 할 수 있는 환경이 필요하다.


스프링부트에서 테스트하기

테스트 환경 생성

테스트 스타터

스프링부트 프로젝트를 생성하면 테스트 스타터는 자동으로 추가되고, 테스트 스타터가 등록되어 있기에 테스트에 필요한 여러 라이브러리들도 자동으로 추가가 된다.

기본 테스트 스타터

스프링부트 프로젝트 생성 시 기본적으로 제공되는 테스트 케이스 소스는 아래와 같다.

package com.example.springboottest;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootTestApplicationTests {

    @Test
    void contextLoads() {
    }

}
  • @SpringBootTest
    SpringBootApplication 어노테이션이 사용자 작성 빈과 자동설정 빈들을 모두 초기화하는 것과 마찬가지로, 이 어노테이션 역시 테스트를 위해 필요한 모든 설정과 빈들을 자동으로 초기화한다.

    • @SpringBootTest 의 속성
      • properties : 테스트 실행 전 테스트에 사용할 프로퍼티들을 키=값 형태로 추가하거나 properties 파일에 설정된 프로퍼티를 재정의한다.
      • classes : 테스트할 클래스들을 등록. 생략 시 애플리케이션에 정의된 모든 빈 생성
      • webEnvironment : 애플리케이션 실행 시 웹관련 환경 설정

예제

application.properties


## Test Property Setting
author.name=TESTER
author.age=24

SpringBootTestApplicationTests.java

package com.example.springboottest;

import com.example.springboottest.controller.BoardController;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = BoardController.class,
        properties = { "author.name=Changed TESTER", "author.age=25" })
class SpringbootTestApplicationTests {
    @Autowired
    private Environment env;


    @Test
    void contextLoads() {
        System.out.println("이름 : " + env.getProperty("author.name"));
        System.out.println("나이 : " + env.getProperty("author.age"));
    }

}

실행 결과는 아래와 같다

class 속성을 설정해주면 불필요한 객체는 메모리에 올라가지 않기 때문에 메모리 낭비를 줄일 수 있다

또 properties속성을 이용하면 application.properties 파일에 설정된 외부 프로퍼티를 재정의할 수 있고, 또는 새로운 프로퍼티를 추가할 수도 있다. (그냥 새로 쓰면 댐)



MockMvc를 이용한 컨트롤러 테스트

Mock : 테스트를 위해 만든 모형
Mocking : 테스트를 위해 실제 객체와 비슷한 모의 객체를 만드는 것
Mock-up : 모킹한 객체를 메모리에서 얻어내는 과정

🤷🏻‍♀️ 왜 굳이 모의 객체를 만드능가?

→ 객체 테스트를 위해서는 테스트 대상 객체가 메모리에 있어야하는데, 생성하는데 복잡한 객체는 테스트가 어렵다. ( 다른 소프트웨어의 도움이 필요한 경우가 있기도 하다 )

따라서 그냥 비슷한 가짜를 만들어서 테스트에 필요한 기능만 가지도록 모킹하면 테스트가 쉬워짐! 모킹한 객체를 이용하면 의존성을 단절시킬 수 있어서 쉽게 테스트 할 수 있다.

웹 애플리케이션 컨트롤러 테스트 시 서블릿 컨테이너를 모킹하기 위해 어노테이션을 이용하면 테스트가 간단해진다.

서블릿 컨테이너를 모킹한다?
→ 웹 환경에서 컨트롤러를 테스트하려면 서블릿 컨테이너가 구동되고, DispatcherServlet 객체가 메모리에 올라가야하는 복잡한 절차가 있다. 이를 모킹을 간단히 만들어주는 것

@WebMvcTest 어노테이션

@WebMvcTest@Controller@RestController가 설정된 클래스들을 찾아 메모리에 생성한다.

@SpringBootTest 와 함께 사용 불가능! (서로의 MockMvc를 모킹해서 충돌 발생)

package com.example.springboottest;

// import ...

@RunWith(SpringRunner.class)
@WebMvcTest
public class BoardControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testHello() throws Exception{
        mockMvc.perform(get("/hello").param("name", "test1")).andExpect(status().isOk()).andExpect(content().string("Hello : test1" )).andDo(print());
    }
}

테스트 결과는 아래와 같다


MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /hello
       Parameters = {name=[test1]}
          Headers = []
             Body = null
    Session Attrs = {}

Handler:
             Type = com.example.springboottest.controller.BoardController
           Method = com.example.springboottest.controller.BoardController#hello(String)

... 
...

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"text/plain;charset=UTF-8", Content-Length:"13"]
     Content type = text/plain;charset=UTF-8
             Body = Hello : test1
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

Process finished with exit code 0

톰캣 서버가 구동되지 않고 테스트를 수행할 수 있다.


@AutoConfigureMockMvc

@SpringBootTest 에는 웹 애플리케이션 테스트를 지원하는 webEnvironment 속성이 있다. 이 속성을 생략하면 기본값으로 MOCK이 설정되어 서블릿 컨테이너가 모킹됨
→ 즉, 테스트 케이스 실행 시 서블릿 컨테이너를 구동하지 않는 것

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) 설정으로 모킹한 객체를 의존성 주입 받기 위해서 @AutoConfigureMockMvc를 추가해줘야 한다.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class BoardControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testHello() throws Exception{
        mockMvc.perform(get("/hello").param("name", "test1")).andExpect(status().isOk()).andExpect(content().string("Hello : test1" )).andDo(print());
    }
}

@WebMvcTest@Controller@RestController가 설정된 클래스들을 찾아 메모리에 생성하는 반면에, @AutoConfigureMockMvc@Controller@RestController 외에도 @Repository, @Service도 찾아서 메모리에 올린다는 점이 차이점이다.

간단하게 컨트롤러만 테스트하는 경우에는 @WebMvcTest 사용하는 것이 좋다



MockMvc 상세

  • MockMvc가 제공하는 perform()
    : 브라우저에서 서버로 요청을 보내듯 컨트롤러를 실행시킨다

    • 인자 : RequestBuilder 객체
      RequestBuilder 객체는 MockMvcRequestBuilders의 정적 메소드를 이용하여 생성한다.

      • MockMvcRequestBuilders의 정적 메소드는 GET, POST, DELETE 등과 매치되는 메소드를 제공한다. (get())
      • 반환값으로 MockHttpServletRequestBuilder 객체 리턴한다. 이 객체에 브라우저가 HTTP 요청 프로토콜에 요청 관련 정보를 설정하듯 다양한 정보를 설정할 수 있다. 반환값으로 다시 MockHttpServletRequestBuilder를 반환하기 때문에 메소드 체이닝이 가능하다
    • 반환값 : ResultActions

  • ResultActions
    : 응답결과를 검증하는 andExpect() 메소드를 제공한다.

    • 인자 : ResultMatcher 객체
      ResultMatcher 객체는 MockMvcResultMatchers에 정의된 정적 메소드를 통해 생성한다.

profile
학생 점심을 좀 차리시길 바랍니다

0개의 댓글