목차
컨텍스트 캐싱
스프링부트를 사용해 개발된 애플리케이션은 구동할 때 애플리케이션 컨텍스트와 필요한 것들을 미리 준비하는 시간이 소요됩니다. Junit5 를 사용하여 통합 테스트를 할 때도 역시 테스트용 애플리케이션 컨텍스트를 준비하는 과정이 필요합니다.
실행해야 할 테스트가 10개 있을 때 각 테스트가 실행 될 때마다 매번 똑같은 준비 작업을 하는 것은 비효율적입니다.
만약 10개의 테스트를 실행하기 위한 준비 작업이 모두 동일하다면 한 번만 작업하고 10개의 테스트에 공통으로 사용하는 것이 합리적이겠죠.
Spring Test Context Framework에서는 이러한 동작 방식을 제공하고 있으며 Context Caching이라 합니다. (Context Management and Caching 👈click!! 공식 문서)
핵심 아이디어
SpringBoot 테스트 시 사용하는 컨텍스트를 재사용하기 위해서는 컨텍스트에 할당된 Bean 집합이 동일해야 합니다.
만약 테스트 클래스에서 @MockBean을 사용하여 Mock 객체를 Bean으로 주입 받는 경우 매번 객체를 새로 생성해서 주입하므로 Bean 집합이 동일하지 않기 때문에 컨텍스트를 재사용하지 않습니다. 매번 컨텍스트를 새로 생성하게 되어 전체 테스트가 매우 느려집니다.
공통으로 사용할 테스트 클래스를 하나 만들고 다른 테스트 클래스에서 상속 받아 사용하면 동일한 컨텍스트를 사용하게 할 수 있습니다.
컨텍스트를 초기화 하는 시간이 한 번만 소요되므로 테스트를 비교적 빠르게 실행할 수 있게 됩니다.
컨트롤러 테스트 코드 예시
테스트를 위해 두 개의 간단한 RestController를 작성해보겠습니다.
RestController 테스트 방법에 대해 간단히 정리했던 글이 있습니다.
필요하다면 참고하시기 바랍니다.
(RestController 테스트하기 (MockMvc) 👈click!!)
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ColorController {
@GetMapping("/color")
public String color() {
return "green";
}
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FruitController {
@GetMapping("/fruit")
public String fruit() {
return "banana";
}
}
컨트롤러 테스트용 부모 클래스 (ControllerTest)
이제 컨트롤러 테스트에서 공통적으로 사용할 수 있는 ControllerTest 클래스를 작성합니다.
스프링부트 애플리케이션을 통합 테스트 할 때는 @SpringBootTest 애너테이션을 사용합니다. 아래와 같이 FoodController, FruitController 클래스를 명시해주면 테스트 컨텍스트에 Bean으로 등록됩니다.
@AutoConfigureMockMvc 애너테이션을 붙여주면 Spring MVC 테스트를 위한 mock 객체를 만들어 주게 되고, 의존성 주입을 받아서 사용할 수 있습니다.
import com.targetcoders.blogtest.controller.ColorController;
import com.targetcoders.blogtest.controller.FruitController;
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.web.servlet.MockMvc;
@AutoConfigureMockMvc
@SpringBootTest(classes = {ColorController.class, FruitController.class})
public class ControllerTest {
@Autowired
protected MockMvc mockMvc;
}
컨트롤러 테스트 클래스 (extends ControllerTest)
FoodController와 FruitController를 Bean으로 등록한 TestContext를 공유하여 사용하기 위해서 아래와 같이 ControllerTest를 상속 받아 사용할 수 있습니다.
import com.targetcoders.common.ControllerTest;
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
public class ColorControllerTest extends ControllerTest {
@Test
void colorTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/color"))
.andExpect(MockMvcResultMatchers.content().string("red"));
}
}
import com.targetcoders.common.ControllerTest;
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
public class FruitControllerTest extends ControllerTest {
@Test
void fruitTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/fruit"))
.andExpect(MockMvcResultMatchers.content().string("banana"));
}
}