Loading...
Spring Framework Reference Documentation 7.0.2의 Context Hierarchies의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
통합 테스트를 작성할 때 로드된 Spring ApplicationContext에 의존하는 경우, 단일 컨텍스트에 대해 테스트하는 것만으로도 충분한 경우가 많습니다. 그러나 때로는 ApplicationContext 인스턴스의 계층 구조에 대해 테스트하는 것이 유익하거나 심지어 필요할 때도 있습니다. 예를 들어 Spring MVC 웹 애플리케이션을 개발하는 경우, 일반적으로 Spring의 ContextLoaderListener에 의해 로드되는 루트 WebApplicationContext와 Spring의 DispatcherServlet에 의해 로드되는 자식 WebApplicationContext를 갖게 됩니다.
이로 인해 부모-자식 컨텍스트 계층 구조가 생성되며, 여기서 공유 컴포넌트와 인프라 설정은 루트 컨텍스트에 선언되고 웹 특화 컴포넌트에 의해 자식 컨텍스트에서 사용됩니다. 또 다른 사용 사례는 Spring Batch 애플리케이션에서 찾을 수 있으며, 여기서는 종종 공유 배치 인프라에 대한 설정을 제공하는 부모 컨텍스트와 특정 배치 잡의 설정을 위한 자식 컨텍스트를 갖게 됩니다.
@ContextHierarchy 어노테이션으로 컨텍스트 설정을 선언하여 컨텍스트 계층 구조를 사용하는 통합 테스트를 작성할 수 있으며, 이는 개별 테스트 클래스 또는 테스트 클래스 계층 구조 내에서 사용할 수 있습니다. 컨텍스트 계층 구조가 테스트 클래스 계층 구조 내의 여러 클래스에 선언된 경우, 컨텍스트 계층 구조에서 특정 이름이 있는 레벨에 대한 컨텍스트 설정을 병합하거나 오버라이드할 수도 있습니다.
계층 구조에서 주어진 레벨에 대한 설정을 병합할 때 설정 리소스 타입(즉, XML 설정 파일 또는 컴포넌트 클래스)은 일관되어야 합니다. 그렇지 않으면 컨텍스트 계층 구조의 서로 다른 레벨이 서로 다른 리소스 타입을 사용하여 구성되는 것은 완전히 허용됩니다.
컨텍스트 계층 구조의 일부로 구성된 컨텍스트를 가진 테스트에서 @DirtiesContext를 사용하는 경우,<br>hierarchyMode플래그를 사용하여 컨텍스트 캐시를 어떻게 비울지 제어할 수 있습니다.<br>자세한 내용은<br>Spring Testing Annotations에 있는@DirtiesContext에 대한 논의와<br>@DirtiesContextjavadoc을 참조하십시오.
이 섹션의 JUnit Jupiter 기반 예제는 컨텍스트 계층 구조 사용을 필요로 하는 통합 테스트에 대한 일반적인 설정 시나리오를 보여줍니다.
컨텍스트 계층 구조를 가진 단일 테스트 클래스
ControllerIntegrationTests는 두 개의 레벨로 구성된 컨텍스트 계층 구조를 선언함으로써 Spring MVC 웹 애플리케이션에 대한 일반적인 통합 테스트 시나리오를 나타냅니다. 하나는 루트 WebApplicationContext( TestAppConfig @Configuration 클래스를 사용하여 로드됨)용이고 다른 하나는 디스패처 서블릿 WebApplicationContext( WebConfig @Configuration 클래스를 사용하여 로드됨)용입니다.
테스트 인스턴스에 자동 주입되는 WebApplicationContext는 자식 컨텍스트(즉, 계층 구조에서 가장 낮은 컨텍스트)용입니다. 다음 목록은 이 설정 시나리오를 보여 줍니다:
1@ExtendWith(SpringExtension.class) 2@WebAppConfiguration 3@ContextHierarchy({ 4 @ContextConfiguration(classes = TestAppConfig.class), 5 @ContextConfiguration(classes = WebConfig.class) 6}) 7class ControllerIntegrationTests { 8 9 @Autowired 10 WebApplicationContext wac; 11 12 // ... 13}
1@ExtendWith(SpringExtension::class) 2@WebAppConfiguration 3@ContextHierarchy( 4 ContextConfiguration(classes = [TestAppConfig::class]), 5 ContextConfiguration(classes = [WebConfig::class])) 6class ControllerIntegrationTests { 7 8 @Autowired 9 lateinit var wac: WebApplicationContext 10 11 // ... 12}
암시적 부모 컨텍스트를 가진 클래스 계층 구조
이 예제의 테스트 클래스는 테스트 클래스 계층 구조 내에서 컨텍스트 계층 구조를 정의합니다. AbstractWebTests는 Spring 기반 웹 애플리케이션에서 루트 WebApplicationContext에 대한 설정을 선언합니다. 그러나 AbstractWebTests는 @ContextHierarchy를 선언하지 않는다는 점에 유의하십시오.
결과적으로 AbstractWebTests의 서브클래스는 선택적으로 컨텍스트 계층 구조에 참여하거나 @ContextConfiguration에 대한 표준 의미를 따를 수 있습니다. SoapWebServiceTests와 RestWebServiceTests는 둘 다 AbstractWebTests를 확장하고 @ContextHierarchy를 사용하여 컨텍스트 계층 구조를 정의합니다. 그 결과 세 개의 애플리케이션 컨텍스트가 로드됩니다(@ContextConfiguration 선언마다 하나씩). 그리고 AbstractWebTests의 설정을 기반으로 로드된 애플리케이션 컨텍스트는 구체 서브클래스에 대해 로드된 각 컨텍스트의 부모 컨텍스트로 설정됩니다. 다음 목록은 이 설정 시나리오를 보여 줍니다:
1@ExtendWith(SpringExtension.class) 2@WebAppConfiguration 3@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml") 4public abstract class AbstractWebTests {} 5 6@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml")) 7public class SoapWebServiceTests extends AbstractWebTests {} 8 9@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml")) 10public class RestWebServiceTests extends AbstractWebTests {}
1@ExtendWith(SpringExtension::class) 2@WebAppConfiguration 3@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml") 4abstract class AbstractWebTests 5 6@ContextHierarchy(ContextConfiguration("/spring/soap-ws-config.xml")) 7class SoapWebServiceTests : AbstractWebTests() 8 9@ContextHierarchy(ContextConfiguration("/spring/rest-ws-config.xml")) 10class RestWebServiceTests : AbstractWebTests()
병합된 컨텍스트 계층 구조 설정을 가진 클래스 계층 구조
이 예제의 클래스는 컨텍스트 계층 구조에서 특정 레벨에 대한 설정을 병합하기 위해 이름이 있는 계층 레벨의 사용을 보여 줍니다. BaseTests는 계층 구조에서 parent와 child라는 두 개의 레벨을 정의합니다. ExtendedTests는 BaseTests를 확장하고 child 계층 레벨에 대한 컨텍스트 설정을 병합하도록 Spring TestContext Framework에 지시하며, 이를 위해 @ContextConfiguration의 name 속성에 선언된 이름이 모두 child가 되도록 합니다.
그 결과 세 개의 애플리케이션 컨텍스트가 로드됩니다. 하나는 /app-config.xml, 하나는 /user-config.xml, 그리고 하나는 {" /user-config.xml", "/order-config.xml"}용입니다. 이전 예제와 마찬가지로 /app-config.xml에서 로드된 애플리케이션 컨텍스트는 /user-config.xml 및 {" /user-config.xml", "/order-config.xml"}에서 로드된 컨텍스트의 부모 컨텍스트로 설정됩니다. 다음 목록은 이 설정 시나리오를 보여 줍니다:
1@ExtendWith(SpringExtension.class) 2@ContextHierarchy({ 3 @ContextConfiguration(name = "parent", locations = "/app-config.xml"), 4 @ContextConfiguration(name = "child", locations = "/user-config.xml") 5}) 6class BaseTests {} 7 8@ContextHierarchy( 9 @ContextConfiguration(name = "child", locations = "/order-config.xml") 10) 11class ExtendedTests extends BaseTests {}
1@ExtendWith(SpringExtension::class) 2@ContextHierarchy( 3 ContextConfiguration(name = "parent", locations = ["/app-config.xml"]), 4 ContextConfiguration(name = "child", locations = ["/user-config.xml"])) 5open class BaseTests {} 6 7@ContextHierarchy( 8 ContextConfiguration(name = "child", locations = ["/order-config.xml"]) 9) 10class ExtendedTests : BaseTests() {}
오버라이드된 컨텍스트 계층 구조 설정을 가진 클래스 계층 구조
이전 예제와 달리, 이 예제는 @ContextConfiguration에서 inheritLocations 플래그를 false로 설정하여 컨텍스트 계층 구조에서 주어진 이름이 있는 레벨에 대한 설정을 오버라이드하는 방법을 보여 줍니다. 결과적으로 ExtendedTests에 대한 애플리케이션 컨텍스트는 /test-user-config.xml에서만 로드되며, 부모는 /app-config.xml에서 로드된 컨텍스트로 설정됩니다. 다음 목록은 이 설정 시나리오를 보여 줍니다:
1@ExtendWith(SpringExtension.class) 2@ContextHierarchy({ 3 @ContextConfiguration(name = "parent", locations = "/app-config.xml"), 4 @ContextConfiguration(name = "child", locations = "/user-config.xml") 5}) 6class BaseTests {} 7 8@ContextHierarchy( 9 @ContextConfiguration( 10 name = "child", 11 locations = "/test-user-config.xml", 12 inheritLocations = false 13)) 14class ExtendedTests extends BaseTests {}
1@ExtendWith(SpringExtension::class) 2@ContextHierarchy( 3 ContextConfiguration(name = "parent", locations = ["/app-config.xml"]), 4 ContextConfiguration(name = "child", locations = ["/user-config.xml"])) 5open class BaseTests {} 6 7@ContextHierarchy( 8 ContextConfiguration( 9 name = "child", 10 locations = ["/test-user-config.xml"], 11 inheritLocations = false 12 )) 13class ExtendedTests : BaseTests() {}
빈 오버라이드를 사용하는 컨텍스트 계층 구조
@ContextHierarchy가 @TestBean, @MockitoBean, @MockitoSpyBean과 같은 빈 오버라이드와 함께 사용될 때, 컨텍스트 계층 구조의 단일 레벨에 오버라이드를 적용하는 것이 바람직하거나 필요할 수 있습니다. 이를 달성하려면 빈 오버라이드가 @ContextConfiguration의 name 속성을 통해 구성된 이름과 일치하는 컨텍스트 이름을 지정해야 합니다.
다음 테스트 클래스는 두 번째 계층 레벨의 이름을 "user-config"로 구성하고 동시에 "user-config"라는 이름의 컨텍스트에서 UserService가 Mockito 스파이로 래핑되도록 지정합니다. 결과적으로 Spring은 "user-config" 컨텍스트에서만 스파이 생성을 시도하고 부모 컨텍스트에서는 스파이 생성을 시도하지 않습니다.
1@ExtendWith(SpringExtension.class) 2@ContextHierarchy({ 3 @ContextConfiguration(classes = AppConfig.class), 4 @ContextConfiguration(classes = UserConfig.class, name = "user-config") 5}) 6class IntegrationTests { 7 8 @MockitoSpyBean(contextName = "user-config") 9 UserService userService; 10 11 // ... 12}
1@ExtendWith(SpringExtension::class) 2@ContextHierarchy( 3 ContextConfiguration(classes = [AppConfig::class]), 4 ContextConfiguration(classes = [UserConfig::class], name = "user-config")) 5class IntegrationTests { 6 7 @MockitoSpyBean(contextName = "user-config") 8 lateinit var userService: UserService 9 10 // ... 11}
컨텍스트 계층 구조의 서로 다른 레벨에서 빈 오버라이드를 적용할 때, 예를 들어 목에 대한 스터빙을 구성하기 위해 테스트 클래스에 모든 빈 오버라이드 인스턴스가 주입되어야 할 수도 있습니다. 그러나 @Autowired는 항상 컨텍스트 계층 구조의 가장 낮은 레벨에서 발견된 일치하는 빈을 주입합니다.
따라서 컨텍스트 계층 구조의 특정 레벨에서 빈 오버라이드 인스턴스를 주입하려면 필드에 적절한 빈 오버라이드 어노테이션을 적용하고 컨텍스트 레벨의 이름을 구성해야 합니다.
다음 테스트 클래스는 계층 레벨의 이름을 "parent"와 "child"로 구성합니다. 또한 각각 "parent"와 "child"라는 이름의 해당 컨텍스트에서 Mockito 목으로 PropertyService 빈을 생성하거나 교체하도록 구성된 두 개의 PropertyService 필드를 선언합니다. 그 결과 "parent" 컨텍스트의 목은 propertyServiceInParent 필드에 주입되고, "child" 컨텍스트의 목은 propertyServiceInChild 필드에 주입됩니다.
1@ExtendWith(SpringExtension.class) 2@ContextHierarchy({ 3 @ContextConfiguration(classes = ParentConfig.class, name = "parent"), 4 @ContextConfiguration(classes = ChildConfig.class, name = "child") 5}) 6class IntegrationTests { 7 8 @MockitoBean(contextName = "parent") 9 PropertyService propertyServiceInParent; 10 11 @MockitoBean(contextName = "child") 12 PropertyService propertyServiceInChild; 13 14 // ... 15}
1@ExtendWith(SpringExtension::class) 2@ContextHierarchy( 3 ContextConfiguration(classes = [ParentConfig::class], name = "parent"), 4 ContextConfiguration(classes = [ChildConfig::class], name = "child")) 5class IntegrationTests { 6 7 @MockitoBean(contextName = "parent") 8 lateinit var propertyServiceInParent: PropertyService 9 10 @MockitoBean(contextName = "child") 11 lateinit var propertyServiceInChild: PropertyService 12 13 // ... 14}
Context Failure Threshold
Dependency Injection of Test Fixtures