개발과정에서 문제를 미리 발견 가능.
리팩토링의 리스크가 줄어듦.
애플리케이션을 가동해서 직접 테스트하는 것보다 테스트를 빠르게 진행할 수 있음.
하나의 명세 문서로서의 기능을 수행.
몇 가지 프레임워크에 맞춰 테스트 코드를 작성하면 좋은 코드를 생상 가능.
코다가 작서된 목적을 명확하게 표현할 수 있으며, 불필요한 내용이 추가되는 것을 방지함.
단위 테스트 : 애플리케이션의 개별 모듈을 독립적으로 테스트하는 방식.
통합 테스트 : 애플리케이션을 구성하는 다양한 모듈을 결합해 전체적인 로직이 의도한 대로 동작하는지 테스트하는 방식.
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
...
package com.springboot.test.controller;
import com.google.gson.Gson;
import com.springboot.test.data.dto.ProductDto;
import com.springboot.test.data.dto.ProductResponseDto;
import com.springboot.test.service.impl.ProductServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
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.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(ProductController.class)
public class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
ProductServiceImpl service;
@Test
@DisplayName("MockMvc를 통한 Product 데이터 가져오기 테스트")
void getProductTest() throws Exception{
given(service.getProduct(123L)).willReturn(
new ProductResponseDto(123L,"pen",5000,200)
);
String productId = "123";
mockMvc.perform(
get("/product?number=" + productId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.number").exists())
.andExpect(jsonPath("$.name").exists())
.andExpect(jsonPath("$.price").exists())
.andExpect(jsonPath("$.stock").exists())
.andDo(print());
verify(service).getProduct(123L);
}
@Test
@DisplayName("Product 데이터 생성 테스트")
void createdProductTest()throws Exception{
given(service.saveProduct(new ProductDto("pen",5000,2000)))
.willReturn(new ProductResponseDto(12315L,"pen",5000,2000));
ProductDto dto = ProductDto.builder()
.name("pen")
.price(5000)
.stock(2000)
.build();
Gson gson = new Gson();
String content = gson.toJson(dto);
mockMvc.perform( post("/product").content(content).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.number").exists())
.andExpect(jsonPath("$.name").exists())
.andExpect(jsonPath("$.price").exists())
.andExpect(jsonPath("$.stock").exists())
.andDo(print());
verify(service).saveProduct(new ProductDto("pen",5000,2000));
}
}
@WebMvcTest(테스트 대상 클래스.class)
웹에서 사용되는 요청과 응답에 대한 테스트를 수행할 수 있음.
대상 클래스만 로드하여 테스트를 수행하며, 대상 클래스를 추가하지 않으면 @Controller, @RestController 등의 컨트롤러 관련 빈 객체가 모두 로드됨.
@MockBean
Mock(가짜) 객체를 생성하여 주입해주는 역할 수행.
Mock 객체는 실제 객체가 아니기 때문에 행위를 수행하지 않는다. 따라서 해당 객체는 개발자가 Mockito의 given() 메서드를 통해 동작을 정의해야 한다.
@Test
테스트 코드가 포함되어 있다고 선언하는 어노테이션.
@Display
테스트 메서드의 이름이 복잡해서 가독성이 떨어질 경우 이 어노테이션을 통해 테스트에 대한 표현을 정의가능.
일반적으로 @WebMvcTest 어노테이션을 사용한 테스트는 슬라이스 테스트라고 한다. 이는 단위 테스트와 통합 테스트의 중간 개념으로 이해하면 된다.