여러 글에서 말한바와 같이, springboot의 테스트 속도를 느리게 만드는 주요 원인은 ApplicationContext이 계속해서 로딩 되는 경우이다.
ApplicationContext의 caching을 적극 활용하여 테스트 속도를 빠르게 하는것이 무엇보다도 최우선 순위라고 볼 수 있다.
물론 ApplicationContext를 띄우지 않고 모든 테스트를 진행하는것이 좋긴 하지만, 테스트를 작성하는것이 어렵고 귀찮아진다. 하지만 이론상으로, 하나의 ApplicationContext로 테스트를 진행할 수 있으면, 정말 빠르게 테스트를 수행 할 수 있다.
특히 테스트 작성시 @MockBean 등을 사용하는 경우는, ApplicationContext를 반드시 생성시키므로, 삼가해야 할 패턴이다.
그렇다면 mocking해야할 bean이 있을 때 어떻게 해야 할까?
예시 코드를 보여주겠다.
@RestController
class ExampleController(
private val exampleService: ExampleService,
) {
@RequestMapping("/sample1")
@GetMapping
fun sample(): String {
return exampleService.execute()
}
}
@Service("exampleService")
class ExampleService {
fun execute(): String {
val result = "this is real ExampleService"
return result
}
}
이런 매우 간단한 contoller & service가 있다. 테스트를 작성해보겠다.
@SpringBootTest
class ExampleControllerTest(
@Autowired
private val exampleService: ExampleService
) {
@Test
fun test1() {
val result =exampleService.execute()
println(result)
}
}
이때, exampleService를 mocking하고 싶다면, mocking한 bean을 더 만들어 주면 된다.
@TestConfiguration
class ExampleConfiguration {
@Autowired
private lateinit var exampleService: ExampleService
@Bean("mockExampleService")
fun mockExampleService(): ExampleService {
val mockService: ExampleService = mockk(relaxed = true)
val mockResult = "This is mock result"
every { mockService.execute() } returns mockResult
return mockService
}
}
여기서 의문이 생길 수도 있다. exampleService를 테스트에서 당장 호출 하고 있으니, 문제가 없는 것 아닌가 하고 말이다.
그렇다면, wrapping class로 테스트를 해보면 된다.
기존 service는 유지하 되, 새로운 컨트롤러와 컴포넌트를 만들어 보자.
@RestController
class ExampleController2(
private val exampleComponent: ExampleComponent,
){
@RequestMapping("/sample2")
@GetMapping
fun sample2(): String {
return exampleComponent.componentExecute()
}
}
@Component
class ExampleComponent(
private val exampleService: ExampleService
) {
fun componentExecute(): String {
return exampleService.execute()
}
}
의문점을 테스트 해 볼 좋은 구조가 완성 되었다.
Controller단을 테스트 할 때, service만을 mocking했을 때, @MockBean처럼 ExampleComponent가 사용하는 service가 mocking 되는 지를 테스트 해볼 수 있게 되었다.
테스트는 다음과 같다.
@ContextConfiguration(classes = [ExampleConfiguration::class])
@SpringBootTest
class ExampleControllerWrappingTest(
@Autowired
private val mockExampleService: ExampleService
) {
@Test
fun test1() {
val result = mockExampleService.execute()
println(result)
}
}
과연 결과는!!

역시나 동작을 잘한다. 왠만해서는 mockbean 대신 귀찮더라도 이러한 형태로 bean을 주입해서 사용하길 바란다.
마지막으로, 정말로 ApplicationContext가 한번만 생성되는지 테스트 3개를 돌려보겠다.

간단하게 applicaitonContext가 한번만 호출 되는 것을 확인 할 수 있다.