본문 바로가기

Spring/단위테스트

3) Spring boot 단위테스트 3부 - REST API 테스트

반응형

REST API 테스를 위해 아래와 같인 간단한 API 테스트 코드를 작성해 봤다.

코드를 보면서 하나씩 알아보자.

 

package com.devracoon.jpa.controller;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;

import org.junit.jupiter.api.Assertions;
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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import com.devracoon.jpa.entity.Product;
import com.devracoon.jpa.service.SampleService;

@WebMvcTest(UserController.class)
class UserControllerTest {

	@MockBean
	private SampleService sampleService;
	
	@Autowired
	private MockMvc mockMvc;

	@Test
	void testGetProduct() throws Exception {

		given(sampleService.getProduct(any()))
				.willReturn(new Product("fc00ddb5-9f05-40a0-beb9-4ea5431379c0", "product-test"));

		final String productId = "fc00ddb5-9f05-40a0-beb9-4ea5431379c0";

		MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/user/getProduct").param("productId", productId)
				.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn();

		Assertions.assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus(), "Incorrect Response Status");

		System.out.println(result.getResponse().getContentAsString());

	}

}

 

@WebMvcTest 는 

@Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor 

class를 스캔해서 테스트가능하게 해준다. 

 

지금은 UserController.class를 직접 지정해주면서 UserController 만 스캔하게 했다.

 

given 는 앞서 1부에서 설명했고 지금은 단순히 api result를 받아서 status를 확인하고 리턴 Object의 값을 확인해 봤다.

 

다음소스는 리턴값을 비교해보는 소스로 변경해보자.

 

package com.devracoon.jpa.controller;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
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;

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.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import com.devracoon.jpa.entity.Product;
import com.devracoon.jpa.service.SampleService;

import lombok.extern.slf4j.Slf4j;

@WebMvcTest(UserController.class)
class UserControllerTest {

	@MockBean
	private SampleService sampleService;
	
	@Autowired
	private MockMvc mockMvc;

	@Test
	void testGetProduct() throws Exception {
		
		Product productStub = new Product("product-test");
		productStub.setProductId("fc00ddb5-9f05-40a0-beb9-4ea5431379c0");
		
		given(sampleService.getProduct(any()))
				.willReturn(productStub);

		final String productId = "fc00ddb5-9f05-40a0-beb9-4ea5431379c0";

//		MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/user/getProduct").param("productId", productId)
//				.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn();
//
//		Assertions.assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus(), "Incorrect Response Status");
//
//		System.out.println(result.getResponse().getContentAsString());
		
		final ResultActions actions = mockMvc.perform(MockMvcRequestBuilders.get("/user/getProduct").param("productId", productId));
		 
        // then
        actions
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.productId").value(productId));

	}

}

위소스에서 변경된점을 보면 api 호출부분을 ResultAction으로 변경했고 

 

해당 actions를 andDo로 실행하고 실행한 결과를 print 옵션으로 console에 찍었고 

andExpect 는 해당 결과에 대한 예측값을 코딩한것이다. 

 

해당 예측값이 다른경우는 테스트가 실패하게 된다.

 

jsonPath로 결과 json 값에 대한 예측값 테스트도 가능하다. 

 

여기서 생각해 봐야하는것이 앞서도 애기했지만 Controller와 Service와 Repository는 하나의 연결된 기능인데

 

각각을 Mock으로 단위테스트를 진행하는것이 의미가 있는 경우도 있을것이고 그렇지 않는 경우도 있을것 같다.

 

단위테스트를 만드는것은 결국은 각 기능의 완성도와 전체의 완성도를 같이 생각해봐야 하는 문제이므로 

 

각 상황에 맞게 적절히 사용해야 테스트가 의미가 있게 되는것 같다.