Java

[Spring Boot] 단위 테스트(JUnit) 사용 방법

cob 2023. 6. 16. 01:18
단위 테스트란?
소프트웨어 개발에서 사용되는 테스트 방법 중 하나로 소프트웨어의 가장 작은 단위인 모듈, 함수, 클래스, 메서드 등의 개별적인 단위를 분리하여 테스트하는 것을 의미한다.
* JUnit
JUnit은 자바 프로그래밍 언어를 위한 단위 테스트 프레임워크로 Spring Boot로 프로젝트를 생성할 때 자동으로 의존성이 포함된다.

 

* 전체 Git 소스 코드
https://github.com/kangilbin/Spring-Boot/tree/master/%EB%8B%A8%EC%9C%84%20%ED%85%8C%EC%8A%A4%ED%8A%B8/demo

 

 

 


1. 의존성 확인(pom.xml)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

 

 


2. 테스트 클래스 생성 방법

2-1) 테스트 클래스를 만들고 싶은 클래스에 Ctrl + Alt + T

테스트 클래스 단축키
생성
테스트 클래스 생성

  • 동일한 경로에 클래스가 만들어진다.

 

 


2. 테스트 코드 작성 방법

2-1) Mock 을 사용한 테스트

Mock이란?
테스트 시 실제 구현 대신 사용되는 가짜 객체로 Mock 객체는 원하는 방식으로 동작하도록 프로그래밍되어 있으며, 실제 의존성을 가지지 않고도 테스트를 수행할 수 있게 해준다.

 Mock MVC(Mock Model-View-Controller)이란?
서블릿 컨테이너를 테스트할 때, 실제 서블릿 컨테이너를 띄우지 않고 Mock 객체를 사용하여 테스트를 수행할 수 있다. Mock 객체를 서블릿을 모방(Mocking)하기 때문이다. DispatcherServlet을 Mocking하고, 요청을 보내는 것처럼 테스트를 진행하게 된다. Mocking된 서블릿에 요청을 보낼 때는 Mock MVC 클라이언트를 사용해야만 한다. Mock MVC는 실제 HTTP 요청과 응답을 모방하는 Mock 객체로, 서블릿 컨테이너를 실행하지 않고도 컨트롤러의 동작을 테스트할 수 있도록 해준다. Mock MVC를 사용하여 테스트를 수행할 때, 테스트 메서드에서 Mock MVC 클라이언트를 생성하고 설정한 후, 원하는 URL에 대한 요청을 보낸다. 그럼 Mock 객체를 통해 처리된 요청에 대한 결과를 얻을 수 있고, 테스트 결과를 검증할 수 있다. 이렇게 Mock 객체와 Mock MVC를 사용하면 실제 서블릿 컨테이너를 실행하지 않고도 서블릿 관련 코드를 테스트할 수 있어 테스트의 속도를 향상하고, 테스트 환경을 관리하기 쉽게 만들어준다.
package com.example.demo.sample;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
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.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) // 기본 값이 Mock
@AutoConfigureMockMvc                                                // Mock MVC 추가
public class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;                               // 1) Mock MVC 주입
    
    @MockBean                                     // 2) Applcation context에 들어있는 SampleService의  
    SampleService mockSampleService;              //    Bean을 여기서 만든 MockBean으로 교체
    
    @Test                                                     // 3) 테스트 코드 작성
    public void hello() throws Exception {
        when(mockSampleService.getName()).thenReturn("baba"); // 4) SampleService 모방하여 값을 교체
    
        mockMvc.perform(get("/hello"))                      // 5) '/hello' 경로로 Get 요청 보내기
                .andExpect(status().isOk())                 // 6) 상태 코드가 200 (OK)인지 확인
                .andExpect(content().string("hello baba"))  // 7) 응답 컨텐츠의 문자열이 'hello baba'인지 확인
                .andDo(print());                            // 8) 수신된 요청과 응답의 내용을 콘솔에 출력
    }
}
  • Controller만 테스트하고 싶을 경우 Service를 MockBean으로 교체하여 사용한다. 

 

테스트 완료

 

 

 

2-2) 톰캣을 사용한 RANDOM_PORT 테스트 방법

실제 서블릿 컨테이너(내장 톰캣)가 사용 가능한 랜덤 포트를 사용해 실행된다.
package com.example.demo.sample;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class SampleControllerTest {

    @Autowired
    TestRestTemplate testRestTemplate;

    @MockBean                                
    SampleService mockSampleService;

    @Test // 2) 테스트 코드 작성
    public void hello() throws Exception {
        when(mockSampleService.getName()).thenReturn("baba"); 

        String result = testRestTemplate.getForObject("/hello", String.class); // 파라미터 : url, Body Type
        assertThat(result).isEqualTo("hello baba");
    }
}

 

 

 

2-3) 톰캣에서 비동기 테스트 방법

WebTestClient는 Spring 5에서 추가된 기능 중 하나로, 비동기 웹 프레임워크인 Spring WebFlux를 위한 테스트 도구로 WebTestClient를 사용하면 요청을 보내고 기다리는 것이 아니라, 응답이 도착하면 콜백을 받을 수 있다.

 

- 의존성 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

 

package com.example.demo.sample;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

import static org.mockito.Mockito.when;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class SampleControllerTest {

    @Autowired
    WebTestClient webTestClient;

    @MockBean
    SampleService mockSampleService;

    @Test // 2) 테스트 코드 작성
    public void hello() throws Exception {
        when(mockSampleService.getName()).thenReturn("baba");

        webTestClient.get().uri("/hello").exchange()
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo("hello baba");
    }
}

 

 

2-4) 슬라이스 테스트

하나의 Bean만 테스트할 수 있다.
package com.example.demo.sample;

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.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;

@RunWith(SpringRunner.class)
@WebMvcTest(SampleController.class)  // 1) SampleController만 테스트 한다.
public class SampleControllerTest {
    @MockBean
    SampleService mockSampleService;

    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception {
        when(mockSampleService.getName()).thenReturn("baba");

        mockMvc.perform(get("/hello"))
                .andExpect(content().string("hello baba"));
    }
}

 

 


3. OutputCapture를 사용해 테스트 로그 확인 방법 

테스트 코드에서 표준 출력(Standard Output) 또는 표준 에러(Standard Error)에 대한 캡처와 검증을 수행하는 기능을 제공하는 클래스로 OutputCapture를 사용하면 테스트 중에 콘솔에 출력되는 내용을 캡처하여 테스트 결과를 검증할 수 있다.

 

3-1) 로그 작성

logger 작성

  • 실행되는 컨트롤러 내부에 로그를 작성한다. logger와 print 둘 다 캡처가 된다.

 

3-2) 테스트 코드 작성 

package com.example.demo.sample;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class SampleControllerTest {

    @Rule
    public OutputCapture outputCapture = new OutputCapture();  // 1) 캡처 추가

    @Autowired
    WebTestClient webTestClient;

    @MockBean
    SampleService mockSampleService;

    @Test // 2) 테스트 코드 작성
    public void hello() throws Exception {
        when(mockSampleService.getName()).thenReturn("baba");

        webTestClient.get().uri("/hello").exchange()
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo("hello baba");

        assertThat(outputCapture.toString()) // 2) 캡처된 내용중 coco, skip이 포함되어 있는지 확인
                .contains("coco")
                .contains("skip");

    }
}

출력 결과

 

반응형