[Spring] Spring Test - Context Management, Context Caching

hyng·2022년 2월 6일
0

embedded redis를 사용하면서 스프링 컨텍스트가 종료되지 않아 embedded redis가 포트 충돌이 생기는 문제가 발생했습니다.
이 부분을 해결하면서 아래와 같은 궁금점이 생겼습니다.

  • 테스트에서 스프링 컨텍스트는 왜 종료되지 않는 걸까?
  • 테스트에서 스프링 컨텍스트가 재사용이 된다면 무슨 기준으로 재사용이 되는 걸까?
  • 스프링 컨텍스트는 그럼 언제 종료되는 걸까?

위와 같은 궁금점으로 스프링공식문서에서 Spring TestContext Framework가 제공하는 Context Management, Context Caching을 정리하게 되었습니다.
틀린 내용이 있다면 댓글로 의견 주시면 감사하겠습니다🙇‍

Context Management

  • Spring TestContext Framework는 ApplicationContext 인스턴스와 WebApplicationContext 인스턴스를 로딩하고 캐싱 하는 역할을 한다.
  • 컨텍스트를 캐싱 한다는 것이 중요한데, 컨텍스트 캐싱으로 인해 객체들이 인스턴스화되는 시간을 줄여줄 수 있다. (자세한 내용은 아래 Context Caching 참조)
  • 일단 컨텍스트가 로딩, 캐싱되면 ApplicationContext는 각각 테스트에서 재사용된다.
  • 따라서 ApplicationContext의 startup time은 test suite마다 오직 한 번만 발생한다.

test suite: 같은 JVM에서 실행되는 모든 테스트들

  • @DirtiesContext: 만약 테스트가 ApplicationContext를 손상시켜 TestContext Framework가 다음 테스트를 실행하기 전에 ApplicationContext를 rebuild 하도록 할 수 있다.
  • class-level에 @ContextConfiguration 어노테이션을 정의해서 application context를 직접 설정할 수 있다.
  • @ContextConfiguration에 xml, groovy script, @Component, @Configuration 등을 명시할 수 있다.
  • @Component 에 포함되는 클래스


    Component Classes
    The term “component class” can refer to any of the following:
    A class annotated with @Configuration.
    A component (that is, a class annotated with @Component, @Service, @Repository, or other stereotype annotations).
    A JSR-330 compliant class that is annotated with javax.inject annotations.
    Any class that contains @Bean-methods.
    Any other class that is intended to be registered as a Spring component (i.e., a Spring bean in the ApplicationContext), potentially taking advantage of automatic autowiring of a single constructor without the use of Spring annotations.
    See the javadoc of @Configuration and @Bean for further information regarding the configuration and semantics of component classes, paying special attention to the discussion of @Bean Lite Mode.

  • 예시
@ExtendWith(SpringExtension.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {

    // this instance will be dependency injected by type
    @Autowired
    HibernateTitleRepository titleRepository;

    @Test
    void findById() {
        Title title = titleRepository.findById(new Long(10));
        assertNotNull(title);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- this bean will be injected into the HibernateTitleRepositoryTests class -->
    <bean id="titleRepository" class="com.foo.repository.hibernate.HibernateTitleRepository">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- configuration elided for brevity -->
    </bean>

</beans>
  • @ContextConfiguration어노테이션 외에도 ApplicationContext initializers를 통해 구성할 수도 있다.
  • 만약 명시적으로 ApplicationContext resource locations 또는 component classes를 정의하지 않는다면 ContextLoader가 default location 또는 default configuration classes에서 컨텍스트를 로드하는 방법을 결정한다.

Context Caching

  • context를 캐싱 해서 다음번에는 다시 생성하지 않고 캐싱 해둔 context를 재사용하는 것이다.
  • 캐싱 한 컨텍스트를 재사용하는 것은 스프링 자체에 오버헤드를 줄여줄 뿐 아니라
    스프링 컨테이너에 의해 객체들이 인스턴스화되는 것으로 인한 startup time을 줄여주고 그에 따라 전체적인 테스트 실행이 빨라지며 개발자의 생산성도 증가한다.
  • 처음 테스트 실행에서 컨텍스트가 로딩, 캐싱 되면 캐싱 된 컨텍스트를 재사용하는 테스트는 더 빨리 실행될 수 있다.
  • configuration parameters의 조합을 key로 사용한다.
  • TestContext Framework는 cache key를 생성하기 위해 다음의 configuration parameters를 사용한다.
  • 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)
  • propertySourceLocations (from @TestPropertySource)
  • propertySourceProperties (from @TestPropertySource)
  • resourceBasePath (from @WebAppConfiguration)
  • 예를 들어 TestClassA가 @ContextConfiguration의 locations를 {"app-config.xml", "test-config.xml"}이라고 명시했다면,
    TestContext Framework는 해당하는 ApplicationContext를 로드하고 해당 locations를 기반으로 key 이하에 static content cache에 컨텍스트를 저장한다.
  • 그리고 만약 TestClassB가 locations를 {"app-config.xml", "test-config.xml"}라고 정의하고 @WebAppConfiguration, 다른 contextLoader, 다른 active profiles, 다른 context initializers, 다른 test property sources, 또는 다른 parent context를 정의하지 않는다면 같은 ApplicationContext가 두 개의 클래스에 공유된다.
  • @MockBean 처리한 빈의 조합이 달라질 때 두개의 클래스가 같은 locations 혹은 같은 classes를 가지더라도 컨텍스트가 공유되지 않을 수 있다. 자세한 내용은 Spring Testing - Context Management and Caching 참조
  • 컨텍스트 캐싱의 이점을 위해서는, 모든 테스트들은 같은 프로세스 또는 같읕 test suite 안에서 실행되어야만 한다.
    컨텍스트는 static variable에 저장되기 때문에, 테스트들이 서로 다른 프로세스에서 실행된다면 static cache는 각 테스트 실행 사이에 삭제되므로 캐싱 메커니즘을 사용할 수가 없다.
    즉, 같은 IDE 안에서 그룹으로 모든 테스트를 실행해야만 캐싱의 이점을 누릴 수 있다.
  • 컨텍스트 캐시의 기본 최대 크기는 32이다.
  • 최대 크기에 도달하면, LRU(가장 오랫동안 참조되지 않은 페이지 교체) policy가 캐시에 존재하는 컨텍스트를 제거하고 종료하는 데 사용된다.
  • Application Context는 다음의 시나리오에서만 종료된다.
    • @DirtiesContext 어노테이션에 의해 종료
    • LRU eviction policy에 따라 캐시에서 자동으로 교체되어 종료
    • JVM이 종료될 때 JVM shut down hook을 통해 종료
profile
공부하고 알게 된 내용을 기록하는 블로그

0개의 댓글