코딩을 할 때 Test하는 것은 매우 중요하다고 요새 생각이 든다. 그래서 이 Test 하는 방법을 잘 알아둬야 잘하는 개발자로 성장할 수 있다고 본다. 솔직히 전에는 테스트를 잘 안했다. 안했다기 보다는 이런 Junit과 같은 Test 도구를 사용하지 않았다. 그냥 console.log로 찍어보거나 log.info로 데이터가 잘 나오나 안나오나 혹은 system.out으로 확인하곤 했다. 혹은 PostMan을 이용해서 요청을 하고 잘 응답이 오나 안오나를 확인했다.

하지만 실무에서 계속 PostMan을 이용해서 Test할 수도 없는 노릇이고 디버깅을 잘하거나 해야한다고 한다. 여담으로 실무가서 sysout 쓰면 바로 초짜티 난다고하느데..... 아무튼 이제 알아보자.

 

일단 여러가지 방법이 있겠지만 Controller를 Junit5에서 어떻게 테스트 할 수 있는지에 대해서 알아도록 하자

사실 service나 repository 같은 경우는 그 로직에 대해서만 test하면 되기 때문에 assertThat을 이용해서 잘 수행이 됐는지 안됐는지만 확인하면 된다.

 

하지만 controller는 조금 다르다. 왜냐하면 요청이 들어오고 그에 대한 응답을 Test 해야하기 때문에 요청도 해줘야하고 그에 맞는 검증을 또 해줘야 하기 때문이다.

 

먼저 코드로 바로 보자

 

package com.study.database.controller;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

import org.hamcrest.Matchers;
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 org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.study.database.domain.Book;
import com.study.database.service.BookService;

import lombok.extern.slf4j.Slf4j;

// 단위 테스트(Controller 관련 로직만 띄우기) Filter, ControllerAdvice
@Slf4j
@WebMvcTest(BookController.class)
public class BookControllerUnitTest {
	
	@Autowired
	private MockMvc mockMvc;
	
	@MockBean // IoC 환경에 bean 등록됨.
	private BookService bookService;
	
	// BDDMockito 패턴
	@Test
	public void save() throws Exception {
		// given (테스트를 하기 위한 준비)
		Book book = new Book(null, "스프링 따라하기", "mollani");
		String content = new ObjectMapper().writeValueAsString(book);
		log.info("===== content 확인 : {}", content);
		when(bookService.saveBook(book)).thenReturn(1);
		
		//when(테스트실행)
		ResultActions resultAction = mockMvc.perform(post("/book")
				.contentType(MediaType.APPLICATION_JSON)
				.content(content)
				.accept(MediaType.APPLICATION_JSON));
		
		// then(검증)
		resultAction
			.andExpect(status().isCreated())
			.andExpect(content().string("1"))
			.andDo(MockMvcResultHandlers.print());
		
	}

 

일단 여기서 Mock이라는 개념을 좀 알고 가야할 거 같다.

Mock 이란

- 실체 객체를 만들어서 테스트하기 어려운 경우에, 가짜 객체를 만들어서 테스트하는 기술

 

MockMvc

- 웹 어플리케이션을 어플리케이션 서버에 배포하지 않고, 테스트용 MVC 환경을 만들어서 요청 및 전송, 응답 기능을 해주는 객체

- 스프링 MVC 테스트의 주요 구성요소로, HTTP 요청을 모킹하여 컨트롤러의 동작을 테스트할 수 있게 해줌.

 

위의 코드에서 중요한 건 두개라 생각한다. @WebMvcTest와 @MockBean

 

@WebMvCTest

- Controller를 테스트하는 데 중점

- Service, Repository 등의 Bean은 로드되지 않음

- 테스트할 Controller 클래스를 지정할 수 있음.

 

@MockBean

- 이것을 사용하면 서비스 빈을 Mocking하여 Controller 동작을 테스트

 

한 번 정리하자면 @WebMvcTest는 Controller를 테스트하는데 중점을 두기에 Service, Repository를 Bean에 등록되지 않는다. 어??? 그러면 Controller에서 Service 로직이 있는데 어떻게 동작하지??라고 의문이 드는데 이때 바로 이용하는 것이 @MockBean이다. 가짜 Service 객체를 넣어주는 거다.

 

그래서 BDDMockito는 Behavior-Driven Development (BDD) 스타일의 테스트 기법에 맞게 Given - When - Then 순으로 Test코드를 짜면 된다.

 

※ ObjectMapper

- Jackson 라이브러리의 주요 클래서이며 Json 처리를 위한 다양한 기능을 제공한다.

 

.writeValueAsString - 주어진 자바 객체를 Json 문자열로 변환

+ Recent posts