본문 바로가기
카테고리 없음

스프링부트 테스트(3) - context caching

by pius712 2024. 7. 23.

context caching 이란?

테스트 시 구성되는 ApplicationContext 는 아래의 구성정보를 바탕으로 캐시 키를 발급한다.

해당 캐시 키를 바탕으로 application context 를 구성하거나 재사용하게 되어서, 테스트시 이러한 구성정보가 다르다면 새로운 컨텍스트가 생성된다.

  • locations (from @ContextConfiguration)
  • classes (from @ContextConfiguration)
  • contextInitializerClasses (from @ContextConfiguration)
  • contextCustomizers (from ContextCustomizerFactory) – this includes @DynamicPropertySource methods as well as various features from Spring Boot’s testing support such as @MockBean and @SpyBean.
  • contextLoader (from @ContextConfiguration)
  • parent (from @ContextHierarchy)
  • activeProfiles (from @ActiveProfiles)
  • propertySourceDescriptors (from @TestPropertySource)
  • propertySourceProperties (from @TestPropertySource)
  • resourceBasePath (from @WebAppConfiguration)

application context 를 매번 새로 로드하게 되면 그만큼 테스크 구성이 오래걸리므로, 테스트 수행 시간에 영향을 준다.

대표적으로, context caching 이 되지 않는 경우에 대한 예시를 살펴보도록 하자.

context caching 이 되지 않는 케이스 예시

예시1. profile 이 다른 경우

만약 아래와 같이, active profile가 서로 다르다면 서로 다른 application context 를 로드하게 된다.

@ActiveProfiles("dev")
@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
class DevTest

@ActiveProfiles("local")
@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
class LocalTest

예시2. mock bean 을 사용하는 경우

@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
class TestMockTest(
    private val testService: TestService,
    @MockBean
    private var testComponent: TestComponent
)

예시3. property source 를 추가한 경우

@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@TestPropertySource(properties = ["test.value=mocked", "test.value2=mocked2"])
class TestMockTest(
    private val testService: TestService,
    private var testComponent: TestComponent
)

해결책

1. test 시 사용되는 context 를 하나로 관리

  • 하나의 annotation 으로 관리
@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@ActiveProfiles("local")
annotation class IntegrationTest()
  • abstract class 로 관리
@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@ActiveProfiles("local")
abstract class IntegrationTest

2. mockBean 사용 자제하기

  • test configuration 을 사용하기

이 경우 주의할 점이 있다.

  1. 각 테스트에 inner class 에 test configuration 을 사용하는 경우, context caching 이 이루어지지 않는다.
  2. IntegrationTest 를 사용하는 모든 테스트 클래스의 TestComponent 가 mock 으로 등록되기 때문에, 명확성이 떨어진다.
@TestConfiguration
class TestConfig {
    @Bean
    @Primary
    fun testComponent(): TestComponent {
        return mockk()
    }
}

@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@ActiveProfiles("local")
@Import(TestConfig::class)
abstract class IntegrationTest

// 아래의 test component 는 mock 객체이다. 
class TestMockTest(
    private val testService: TestService,
    private val testComponent: TestComponent
):IntegrationTest()
  • 테스트 대상 (sut) 을 조립하기

아래와 같이 mock 으로 만들 대상 객체만 mock 객체로 생성 후, sut 를 생성할 수 있다.

class TestMockTest:IntegrationTest() {
    private lateinit var testService: TestService
    private lateinit var testComponent: TestComponent

    @BeforeEach
    fun setUp() {
        testComponent = mockk<TestComponent>()
        testService = TestService(testComponent)
    }

    @Test
    fun test() {
        every { testComponent.test() } returns "test"
        testService.test()
    }
}