테스트
이 장에서는 통합 테스트에 대한 Spring의 지원과 단위 테스트에 대한 모범 사례를 다룹니다. Spring 팀은 테스트 주도 개발(TDD)을 옹호합니다. Spring 팀은 제어의 역전(IoC)을 올바르게 사용하면 단위 테스트와 통합 테스트가 확실히 더 쉬워진다는 것을 발견했습니다(클래스에 세터 메서드와 적절한 생성자가 있으면 서비스 로케이터 레지스트리 및 유사한 구조를 설정할 필요 없이 테스트에서 이들을 쉽게 연결할 수 있다는 점에서).
섹션 요약
Spring 테스트 소개
테스트는 엔터프라이즈 소프트웨어 개발의 필수적인 부분입니다. 이 장에서는 단위 테스트에 IoC 원칙이 추가하는 가치와 통합 테스트에 대한 Spring 프레임워크의 지원의 이점에 중점을 둡니다. (엔터프라이즈에서의 테스트에 대한 자세한 내용은 이 참조 설명서의 범위를 벗어납니다.)
Unit Testing
종속성 주입을 사용하면 기존 J2EE / Java EE 개발보다 코드가 컨테이너에 덜 의존하게 됩니다. 애플리케이션을 구성하는 POJO는 Spring이나 다른 컨테이너 없이 새연산자를 사용하여 인스턴스화된 객체를 사용하여 JUnit 또는 TestNG 테스트에서 테스트할 수 있어야 합니다. 모의 객체(다른 유용한 테스트 기술과 함께)를 사용하여 코드를 개별적으로 테스트할 수 있습니다. Spring의 아키텍처 권장 사항을 따르면 코드베이스의 깔끔한 계층화 및 구성 요소화로 인해 단위 테스트가 더 쉬워집니다. 예를 들어, 단위 테스트를 실행하는 동안 영구 데이터에 액세스할 필요 없이 DAO 또는 리포지토리 인터페이스를 스터빙하거나 모킹하여 서비스 계층 객체를 테스트할 수 있습니다.
실제 단위 테스트는 일반적으로 설정할 런타임 인프라가 없기 때문에 매우 빠르게 실행됩니다. 개발 방법론의 일부로 실제 단위 테스트를 강조하면 생산성을 높일 수 있습니다. 이 테스트 장의 이 섹션은 IoC 기반 애플리케이션에 대한 효과적인 단위 테스트를 작성하는 데 도움이 되지 않을 수도 있습니다. 그러나 특정 단위 테스트 시나리오의 경우 Spring 프레임워크는 모의 객체와 테스트 지원 클래스를 제공하며, 이 장에서 설명합니다.
Mock Objects
Spring에는 모킹 전용 패키지가 다수 포함되어 있습니다:
Environment
Org.springframework.mock.env 패키지에는Environment 및 PropertySource 추상화의 모의 구현이 포함되어 있습니다(빈 정의 프로파일및 PropertySource 추상화 참조).MockEnvironment 및 MockPropertySource는 환경별 속성에 의존하는 코드에 대한 컨테이너 외부 테스트를 개발하는 데 유용합니다.
JNDI
Org.springframework.mock.jndi 패키지에는 테스트 스위트 또는 독립형 애플리케이션을 위한 간단한 JNDI 환경을 설정하는 데 사용할 수 있는 JNDI SPI의 부분적인 구현이 포함되어 있습니다. 예를 들어 테스트 코드에서 JDBC DataSource 인스턴스가 Jakarta EE 컨테이너에서와 동일한 JNDI 이름에 바인딩되면 테스트 시나리오에서 애플리케이션 코드와 구성을 모두 수정 없이 재사용할 수 있습니다.
Org.springframework.mock.jndi 패키지의 모의 JNDI 지원은 Simple-JNDI와 같은 타사의 완전한 솔루션을 위해 Spring Framework 5.2부터 공식적으로 더 이상 사용되지 않습니다 |
서블릿 API
Org.springframework.mock.web 패키지에는 웹 컨텍스트, 컨트롤러 및 필터를 테스트하는 데 유용한 포괄적인 Servlet API 모의 객체 세트가 포함되어 있습니다. 이러한 모의 객체는 Spring의 웹 MVC 프레임워크와 함께 사용하기 위한 것으로, 일반적으로 동적 모의 객체(예: EasyMock) 또는 대체 Servlet API 모의 객체(예: MockObjects)보다 사용하기 더 편리합니다.
Spring Framework 6.0 이후, org.springframework.mock.web의 모의 객체는 Servlet 6.0 API를 기반으로 합니다 |
Spring MVC 테스트 프레임워크는 모의 Servlet API 객체를 기반으로 구축되어 Spring MVC에 대한 통합 테스트 프레임워크를 제공합니다. MockMvc를 참조하십시오.
Spring 웹 리액티브
Org.springframework.mock.http.server.reactive 패키지에는 WebFlux 애플리케이션에서 사용할 수 있는 ServerHttpRequest 및 ServerHttpResponse의 모의 구현이 포함되어 있습니다.Org.springframework.mock.web.server 패키지에는 이러한 모의 요청 및 응답 객체에 의존하는 모의 ServerWebExchange가 포함되어 있습니다.
모의 서버 요청과 모의 서버 응답은 모두 서버별 구현과 동일한 추상 베이스 클래스에서 확장되며 동작을 공유합니다. 예를 들어 모의 요청은 일단 생성되면 변경할 수 없지만 ServerHttpRequest의 mutate() 메서드를 사용하여 수정된 인스턴스를 생성할 수 있습니다.
모의 응답이 쓰기 계약을 올바르게 구현하고 쓰기 완료 핸들(즉, Mono<Void>)을 반환하기 위해 기본적으로 데이터를 버퍼링하고 테스트에서 어설션에 사용할 수 있도록 하는캐시().then()과 함께 Flux를 사용합니다. 애플리케이션은 사용자 정의 쓰기 함수(예: 무한 스트림을 테스트하기 위해)를 설정할 수 있습니다.
WebTestClient는 모의 요청 및 응답을 기반으로 구축되어 HTTP 서버 없이 WebFlux 애플리케이션을 테스트할 수 있도록 지원합니다. 이 클라이언트는 실행 중인 서버를 사용한 엔드투엔드 테스트에도 사용할 수 있습니다.
Unit Testing Support Classes
Spring에는 단위 테스트에 도움이 되는 여러 클래스가 포함되어 있습니다. 이 클래스들은 두 가지 범주로 나뉩니다:
일반 테스트 유틸리티
Org.springframework.test.util 패키지에는 단위 및 통합 테스트에 사용할 수 있는 몇 가지 범용 유틸리티가 포함되어 있습니다.
AopTestUtils는 AOP 관련 유틸리티 메서드의 모음입니다. 이러한 메서드를 사용하여 하나 이상의 Spring 프록시 뒤에 숨겨진 기본 대상 객체에 대한 참조를 얻을 수 있습니다. 예를 들어, EasyMock 또는 Mockito와 같은 라이브러리를 사용하여 빈을 동적 모의로 구성하고 모의가 Spring 프록시로 래핑된 경우, 기대치를 구성하고 검증을 수행하기 위해 기본 모의에 직접 액세스해야 할 수 있습니다. Spring의 핵심 AOP 유틸리티에 대해서는 AopUtils 및AopProxyUtils를 참조하세요.
ReflectionTestUtils는 리플렉션 기반 유틸리티 메서드의 모음입니다. 다음과 같은 사용 사례에 대한 애플리케이션 코드를 테스트할 때 상수 값을 변경하거나, 비공개 필드를 설정하거나, 비공개 설정자 메서드를 호출하거나, 비공개 구성 또는 라이프사이클 콜백 메서드를 호출해야 하는 테스트 시나리오에서 이러한 메서드를 사용할 수 있습니다:
- 도메인 엔티티의 속성에 대한 공개 세터 메서드가 아닌 비공개 또는 보호 필드 액세스를 허용하는 ORM 프레임워크(예: JPA 및 Hibernate).
- 비공개 또는 보호 필드, 세터 메서드 및 구성 메서드에 대한 종속성 주입을 제공하는 Spring의 어노테이션(예: @Autowired, @Inject 및 @Resource)에 대한 지원.
- 라이프사이클 콜백 메서드에 @PostConstruct 및 @PreDestroy와 같은 어노테이션을 사용합니다.
TestSocketUtils는 통합 테스트 시나리오에서 사용할 수 있도록 로컬 호스트에서 사용 가능한 TCP 포트를 찾기 위한 간단한 유틸리티입니다.
TestSocketUtils는 사용 가능한 임의 포트에서 외부 서버를 시작하는 통합 테스트에 사용할 수 있습니다. 그러나 이러한 유틸리티는 지정된 포트의 후속 가용성에 대해 보장하지 않으므로 신뢰할 수 없습니다. 서버에 사용 가능한 로컬 포트를 찾기 위해TestSocketUtils를 사용하는 대신 서버가 선택하거나 운영 체제에서 할당하는 임의의 임시 포트에서 시작하는 서버의 기능을 사용하는 것이 좋습니다. 해당 서버와 상호 작용하려면 서버에 현재 사용 중인 포트를 쿼리해야 합니다.
|
Spring MVC 테스트 유틸리티
Org.springframework.test.web 패키지에는 Spring MVC ModelAndView 객체를 처리하는 단위 테스트를 위해 JUnit, TestNG 또는 기타 테스트 프레임워크와 함께 사용할 수 있는ModelAndViewAssert가 포함되어 있습니다.
Spring MVC 컨트롤러 단위 테스트
Spring MVC 컨트롤러 클래스를 POJO로 단위 테스트하려면 Spring의Servlet API 모의에서 ModelAndViewAssert를 MockHttpServletRequest, MockHttpSession 등과 결합하여 사용하세요. Spring MVC에 대한 WebApplicationContext구성과 함께 Spring MVC 및 REST 컨트롤러 클래스를 철저히 통합 테스트하려면 대신Spring MVC 테스트 프레임워크를 사용하세요 |
Integration Testing
애플리케이션 서버에 배포하거나 다른 엔터프라이즈 인프라에 연결하지 않고도 일부 통합 테스트를 수행할 수 있는 것이 중요합니다. 이렇게 하면 다음과 같은 사항을 테스트할 수 있습니다:
- Spring IoC 컨테이너 컨텍스트의 올바른 배선.
- JDBC 또는 ORM 도구를 사용한 데이터 액세스. 여기에는 SQL 문의 정확성, 최대 절전 모드 쿼리, JPA 엔티티 매핑 등이 포함될 수 있습니다.
Spring 프레임워크는스프링 테스트 모듈에서 통합 테스트를 위한 최고 수준의 지원을 제공합니다. 실제 JAR 파일의 이름에는 릴리스 버전이 포함될 수 있으며, 어디서 가져왔는지에 따라 긴 org.springframework.test 형식일 수도 있습니다(자세한 설명은 종속성 관리 섹션참조). 이 라이브러리에는 Spring 컨테이너를 사용한 통합 테스트에 유용한 클래스가 포함된 org.springframework.test 패키지가 포함되어 있습니다. 이 테스트는 애플리케이션 서버나 다른 배포 환경에 의존하지 않습니다. 이러한 테스트는 단위 테스트보다 실행 속도가 느리지만 애플리케이션 서버로의 배포에 의존하는 동등한 Selenium 테스트나 원격 테스트보다는 훨씬 빠릅니다.
단위 테스트 및 통합 테스트 지원은 어노테이션 기반Spring TestContext 프레임워크의 형태로 제공됩니다. TestContext 프레임워크는 사용 중인 실제 테스트 프레임워크에 구애받지 않으므로 JUnit, TestNG 등을 포함한 다양한 환경에서 테스트를 계측할 수 있습니다.
다음 섹션에서는 Spring의 통합 지원의 높은 수준의 목표에 대한 개요를 제공하고, 이 장의 나머지 부분에서는 전용 주제에 중점을 둡니다:
Goals of Integration Testing
Spring의 통합 테스트 지원에는 다음과 같은 주요 목표가 있습니다:
- 테스트 간에 Spring IoC 컨테이너 캐싱을 관리합니다.
- 테스트 픽스처 인스턴스의 종속성 주입을 제공합니다.
- 통합 테스트에 적합한 트랜잭션 관리 제공.
- 개발자가 통합 테스트를 작성하는 데 도움이 되는 Spring 전용 베이스 클래스 제공.
다음 몇 섹션에서는 각 목표를 설명하고 구현 및 구성 세부 정보에 대한 링크를 제공합니다.
컨텍스트 관리 및 캐싱
스프링 테스트컨텍스트 프레임워크는 스프링애플리케이션컨텍스트 인스턴스와 웹 애플리케이션컨텍스트 인스턴스의 일관된 로딩과 해당 컨텍스트의 캐싱을 제공합니다. 로드된 컨텍스트의 캐싱을 지원하는 것이 중요한 이유는 시작 시간이 문제가 될 수 있는데, 이는 Spring 자체의 오버헤드 때문이 아니라 Spring 컨테이너에 의해 인스턴스화된 객체가 인스턴스화되는 데 시간이 걸리기 때문입니다. 예를 들어, 50~100개의 최대 절전 모드 매핑 파일이 있는 프로젝트의 경우 매핑 파일을 로드하는 데 10~20초가 걸릴 수 있으며, 모든 테스트 픽스처에서 모든 테스트를 실행하기 전에 이러한 비용이 발생하면 전체 테스트 실행 속도가 느려져 개발자의 생산성이 저하될 수 있습니다.
테스트 클래스는 일반적으로 XML 또는 Groovy 구성 메타데이터를 위한 리소스 위치 배열(주로 클래스 경로에 있음) 또는 애플리케이션을 구성하는 데 사용되는 컴포넌트 클래스 배열을 선언합니다. 이러한 위치 또는 클래스는 프로덕션 배포를 위한 web.xml 또는 기타 구성 파일에 지정된 것과 동일하거나 유사합니다.
기본적으로 로드되면 구성된 ApplicationContext는 각 테스트에 재사용됩니다. 따라서 설정 비용은 테스트 스위트당 한 번만 발생하며 후속 테스트 실행이 훨씬 빨라집니다. 여기서 "테스트 스위트"라는 용어는 동일한 JVM에서 실행되는 모든 테스트(예: 특정 프로젝트 또는 모듈에 대한 모든 테스트가 Ant, Maven 또는 Gradle 빌드에서 실행됨)를 의미합니다. 드물지만 테스트가 애플리케이션 컨텍스트를 손상시켜 다시 로드해야 하는 경우(예: 빈 정의 또는 애플리케이션 객체의 상태 수정), 다음 테스트를 실행하기 전에 구성을 다시 로드하고 애플리케이션 컨텍스트를 다시 빌드하도록 TestContext 프레임워크를 구성할 수 있습니다.
테스트 픽스처의 종속성 주입
TestContext 프레임워크가 애플리케이션 컨텍스트를 로드할 때 종속성 주입을 사용하여 테스트 클래스의 인스턴스를 선택적으로 구성할 수 있습니다. 이는 애플리케이션 컨텍스트에서 미리 구성된 빈을 사용하여 테스트 픽스처를 설정하는 편리한 메커니즘을 제공합니다. 여기서 큰 장점은 다양한 테스트 시나리오(예: Spring 관리 객체 그래프, 트랜잭션 프록시, 데이터 소스 인스턴스 등 구성)에서 애플리케이션 컨텍스트를 재사용할 수 있으므로 개별 테스트 케이스에 대해 복잡한 테스트 픽스처 설정을 중복할 필요가 없다는 것입니다.
예를 들어 Title 도메인 엔티티에 대한 데이터 액세스 로직을 구현하는 클래스(HibernateTitleRepository)가 있는 시나리오를 생각해 봅시다. 다음 영역을 테스트하는 통합 테스트를 작성하려고 합니다:
- Spring 구성: 기본적으로HibernateTitleRepository 빈의 구성과 관련된 모든 것이 올바르고 존재하나요?
- 최대 절전 모드 매핑 파일 구성: 모든 것이 올바르게 매핑되어 있고 올바른 지연 로딩 설정이 적용되어 있나요?
- HibernateTitleRepository의 로직: 이 클래스의 구성된 인스턴스가 예상대로 작동하나요?
TestContext 프레임워크를 사용한 테스트 픽스처의 종속성 주입을 참조하세요.
트랜잭션 관리
실제 데이터베이스에 액세스하는 테스트의 일반적인 문제 중 하나는 지속성 저장소의 상태에 미치는 영향입니다. 개발 데이터베이스를 사용하는 경우에도 상태 변경이 향후 테스트에 영향을 미칠 수 있습니다. 또한 영구 데이터 삽입 또는 수정과 같은 많은 작업은 트랜잭션 외부에서 수행(또는 확인)할 수 없습니다.
테스트 컨텍스트 프레임워크는 이 문제를 해결합니다. 기본적으로 이 프레임워크는 각 테스트에 대해 트랜잭션을 생성하고 롤백합니다. 트랜잭션의 존재를 가정할 수 있는 코드를 작성할 수 있습니다. 테스트에서 트랜잭션으로 프록시된 객체를 호출하면 구성된 트랜잭션 의미론에 따라 올바르게 동작합니다. 또한 테스트 메서드가 테스트를 위해 관리되는 트랜잭션 내에서 실행되는 동안 선택한 테이블의 내용을 삭제하면 기본적으로 트랜잭션이 롤백되고 데이터베이스는 테스트 실행 이전 상태로 돌아갑니다. 트랜잭션 지원은 테스트의 애플리케이션 컨텍스트에 정의된 PlatformTransactionManager 빈을 사용하여 테스트에 제공됩니다.
트랜잭션을 커밋하려는 경우(드물지만 특정 테스트가 데이터베이스를 채우거나 수정하려는 경우 가끔 유용함)@Commit 어노테이션을 사용하여 트랜잭션을 롤백하는 대신 커밋하도록 테스트 컨텍스트 프레임워크에 지시할 수 있습니다.
테스트 컨텍스트 프레임워크를 사용한 트랜잭션 관리를 참조하세요.
통합 테스트를 위한 지원 클래스
Spring TestContext 프레임워크는 통합 테스트 작성을 간소화하는 몇 가지 추상 지원 클래스를 제공합니다. 이러한 기본 테스트 클래스는 테스트 프레임워크에 대한 잘 정의된 훅과 편리한 인스턴스 변수 및 메서드를 제공하여 사용자가 액세스할 수 있도록 합니다:
- 명시적 빈 조회를 수행하거나 컨텍스트의 전체 상태를 테스트하기 위한 ApplicationContext.
- SQL 문을 실행하여 데이터베이스를 쿼리하기 위한 JdbcTemplate. 이러한 쿼리를 사용하여 데이터베이스 관련 애플리케이션 코드의 실행 전후에 데이터베이스 상태를 확인할 수 있으며, Spring은 이러한 쿼리가 애플리케이션 코드와 동일한 트랜잭션 범위에서 실행되도록 보장합니다. ORM 도구와 함께 사용하는 경우 오탐을 피해야 합니다.
또한 프로젝트에 특정한 인스턴스 변수와 메서드를 사용하여 애플리케이션 전체에 대한 사용자 지정 수퍼클래스를 만들 수도 있습니다.
테스트 컨텍스트 프레임워크에 대한 지원 클래스를 참조하세요.
JDBC Testing Support
JdbcTestUtils
Org.springframework.test.jdbc 패키지에는 표준 데이터베이스 테스트 시나리오를 간소화하기 위한 JDBC 관련 유틸리티 함수 모음인 JdbcTestUtils가 포함되어 있습니다. 구체적으로, JdbcTestUtils는 다음과 같은 정적 유틸리티 메서드를 제공합니다.
- countRowsInTable(..): 주어진 테이블의 행 수를 계산합니다.
- countRowsInTableWhere(..): 제공된 WHERE 절을 사용하여 지정된 테이블의 행 수를 계산합니다.
- deleteFromTables(..): 지정된 테이블에서 모든 행을 삭제합니다.
- deleteFromTableWhere(..): 제공된WHERE 절을 사용하여 지정된 테이블에서 행을 삭제합니다.
- dropTables(..): 지정된 테이블을 삭제합니다.
AbstractTransactionalJUnit4SpringContextTests및 AbstractTransactionalTestNGSpringContextTests는JdbcTestUtils에서 앞서 언급한 메서드에 위임하는 편의 메서드를 제공합니다.
|
Embedded Databases
Spring-jdbc 모듈은 데이터베이스와 상호 작용하는 통합 테스트에 사용할 수 있는 임베디드 데이터베이스 구성 및 실행을 지원합니다. 자세한 내용은 임베디드 데이터베이스 지원및 임베디드 데이터베이스를 사용한 데이터 액세스 로직 테스트를 참조하세요.
스프링 테스트 컨텍스트 프레임워크
Spring TestContext 프레임워크( org.springframework.test.context패키지에 위치)는 사용 중인 테스트 프레임워크와 무관한 일반적인 어노테이션 기반 단위 및 통합 테스트 지원을 제공합니다. 또한 TestContext 프레임워크는 어노테이션 기반 구성을 통해 재정의할 수 있는 합리적인 기본값을 통해 구성보다 규칙을 매우 중요하게 여깁니다.
일반적인 테스트 인프라 외에도 TestContext 프레임워크는 JUnit 4, JUnit Jupiter(일명 JUnit 5) 및 TestNG에 대한 명시적인 지원을 제공합니다. JUnit 4와 TestNG의 경우 Spring은 추상 지원 클래스를 제공합니다. 또한, Spring은 소위 POJO 테스트 클래스를 작성할 수 있는 JUnit 4용 사용자 정의 JUnit Runner 및 사용자 정의 JUnit 규칙과 JUnit Jupiter용 사용자 정의 확장 기능을 제공합니다. POJO 테스트 클래스는 추상 지원 클래스와 같은 특정 클래스 계층 구조를 확장할 필요가 없습니다.
다음 섹션에서는 TestContext 프레임워크의 내부에 대한 개요를 제공합니다. 프레임워크 사용에만 관심이 있고 사용자 정의 리스너 또는 사용자 정의 로더로 확장하는 데 관심이 없는 경우 구성(컨텍스트 관리, 종속성주입, 트랜잭션 관리), 지원 클래스 및주석 지원 섹션으로 바로 이동하세요.
섹션 요약
Key Abstractions
프레임워크의 핵심은 테스트 컨텍스트 관리자 클래스와테스트 컨텍스트, 테스트 실행 리스너, 스마트 컨텍스트 로더 인터페이스로 구성됩니다. 각 테스트 클래스에 대해TestContextManager가 생성됩니다(예: JUnit Jupiter의 단일 테스트 클래스 내에서 모든 테스트 메서드를 실행하기 위해). TestContextManager는 차례로 현재 테스트의 컨텍스트를 보유하는 TestContext를 관리합니다. 또한테스트가 진행됨에 따라 테스트 컨텍스트의 상태를 업데이트하고 종속성 주입, 트랜잭션 관리 등을 통해 실제 테스트 실행을 계측하는 TestExecutionListener 구현에 위임합니다.스마트 컨텍스트 로더는 지정된 테스트 클래스에 대한 애플리케이션 컨텍스트 로드를 담당합니다. 다양한 구현에 대한 자세한 정보와 예제는 javadoc 및 Spring 테스트 스위트를 참조하세요.
TestContext
TestContext는 테스트가 실행되는 컨텍스트를 캡슐화하며(사용 중인 실제 테스트 프레임워크와 무관), 해당 테스트 인스턴스에 대한 컨텍스트 관리 및 캐싱 지원을 제공합니다. 또한 TestContext는 요청이 있을 경우 ApplicationContext를 로드하기 위해SmartContextLoader에 위임합니다.
TestContextManager
TestContextManager는 Spring 테스트 컨텍스트 프레임워크의 주요 진입점이며, 단일 테스트 컨텍스트를 관리하고 잘 정의된 테스트 실행 지점에서 등록된 각테스트 실행 리스너에 이벤트를 시그널링하는 역할을 담당합니다:
- 특정 테스트 프레임워크의 "클래스 전" 또는 "모든 메서드 전" 메서드 이전.
- 테스트 인스턴스 사후 처리.
- 특정 테스트 프레임워크의 "전" 또는 "각" 메서드 이전.
- 테스트 메서드 실행 직전이지만 테스트 설정 이후.
- 테스트 메서드 실행 직후이지만 테스트 해체 전.
- 특정 테스트 프레임워크의 "이후" 또는 "각 이후" 메서드 이후.
- 특정 테스트 프레임워크의 '애프터 클래스' 또는 '애프터 올' 메서드 이후.
TestExecutionListener
TestExecutionListener는 리스너가 등록된 테스트 컨텍스트 관리자가 게시한 테스트 실행 이벤트에 반응하기 위한 API를 정의합니다. 테스트 실행 리스너 구성을 참조하십시오.
Context Loaders
ContextLoader는 스프링 테스트컨텍스트 프레임워크에서 관리하는 통합 테스트를 위한 애플리케이션컨텍스트를 로드하기 위한 전략 인터페이스입니다. 컴포넌트 클래스, 활성 빈 정의 프로파일, 테스트 속성 소스, 컨텍스트 계층 구조 및WebApplicationContext 지원을 제공하려면 이 인터페이스 대신SmartContextLoader를 구현해야 합니다.
SmartContextLoader는 원래의 최소 ContextLoader SPI를 대체하는 ContextLoader 인터페이스의 확장입니다. 구체적으로 SmartContextLoader는 리소스 위치, 컴포넌트 클래스 또는 컨텍스트 초기화기를 처리하도록 선택할 수 있습니다. 또한,스마트 컨텍스트 로더는 로드하는 컨텍스트에서 활성 빈 정의 프로파일을 설정하고 프로퍼티 소스를 테스트할 수 있습니다.
Spring은 다음과 같은 구현을 제공합니다:
- DelegatingSmartContextLoader: 두 가지 기본 로더 중 하나로, 테스트 클래스에 대해 선언된 구성 또는 기본 위치 또는 기본 구성 클래스의 존재 여부에 따라 내부적으로 AnnotationConfigContextLoader, GenericXmlContextLoader 또는GenericGroovyXmlContextLoader에 위임합니다. Groovy 지원은 클래스 경로에 Groovy가 있는 경우에만 활성화됩니다.
- WebDelegatingSmartContextLoader: 두 가지 기본 로더 중 하나로, 테스트 클래스에 대해 선언된 구성이나 기본 위치 또는 기본 구성 클래스의 존재 여부에 따라 내부적으로 AnnotationConfigWebContextLoader, GenericXmlWebContextLoader 또는GenericGroovyXmlWebContextLoader에 위임합니다. 웹 컨텍스트 로더는 테스트 클래스에 @WebAppConfiguration이 있는 경우에만 사용됩니다. Groovy 지원은 Groovy가 클래스 경로에 있는 경우에만 활성화됩니다.
- AnnotationConfigContextLoader: 컴포넌트 클래스에서 표준 ApplicationContext를 로드합니다.
- AnnotationConfigWebContextLoader: 컴포넌트 클래스에서 웹 애플리케이션 컨텍스트를 로드합니다.
- GenericGroovyXmlContextLoader: Groovy 스크립트 또는 XML 구성 파일인 리소스 위치에서 표준 ApplicationContext를 로드합니다.
- GenericGroovyXmlWebContextLoader: Groovy 스크립트 또는 XML 구성 파일인 리소스 위치에서 WebApplicationContext를 로드합니다.
- GenericXmlContextLoader: XML 리소스 위치에서 표준 ApplicationContext를 로드합니다.
- GenericXmlWebContextLoader: XML 리소스 위치에서 웹 애플리케이션 컨텍스트를 로드합니다.
테스트 컨텍스트 프레임워크 부트스트랩
스프링 테스트컨텍스트 프레임워크의 내부에 대한 기본 구성은 모든 일반적인 사용 사례에 충분합니다. 그러나 개발팀이나 타사 프레임워크에서 기본 ContextLoader를 변경하거나, 사용자 정의 TestContext 또는 ContextCache를 구현하거나, 기본ContextCustomizerFactory 및 TestExecutionListener 구현 집합을 보강하는 등의 작업을 하고자 하는 경우가 있습니다. 테스트 컨텍스트 프레임워크의 작동 방식을 저수준으로 제어하기 위해 Spring은 부트스트랩 전략을 제공합니다.
테스트컨텍스트부트스트랩퍼는 테스트컨텍스트 프레임워크의 부트스트랩을 위한 SPI를 정의합니다. 테스트 컨텍스트부트스트래퍼는 테스트 컨 텍스트 관리자가 현재 테스트에 대한테스트 실행 리스너 구현을 로드하고 관리하는테스트 컨텍스트를 빌드하는 데 사용됩니다. 직접 또는 메타 어노테이션으로 @BootstrapWith를 사용하여 테스트 클래스(또는 테스트 클래스 계층 구조)에 대한 사용자 지정 부트스트랩 전략을 구성할 수 있습니다. 부트스트랩퍼를 명시적으로 구성하지 않은 경우@BootstrapWith를 사용하여 부트스트랩퍼를 구성하면 @WebAppConfiguration의 존재 여부에 따라 DefaultTestContextBoot스트랩퍼 또는WebTestContextBoot스트랩퍼가 사용됩니다.
테스트 컨텍스트 부트스트래퍼 SPI는 향후 (새로운 요구 사항을 수용하기 위해) 변경될 가능성이 높으므로 구현자는 이 인터페이스를 직접 구현하지 말고 대신 추상 테스트 컨텍스트 부트스트래퍼 또는 그 구체적인 하위 클래스 중 하나를 확장하는 것이 좋습니다.
TestExecutionListener Configuration
Spring은 기본적으로 다음과 같은 순서로 등록되는 다음과 같은 TestExecutionListener 구현을 제공합니다:
- 서블릿테스트실행 리스너:웹 애플리케이션 컨텍스트에 대한 서블릿 API 모의 서비스를 구성합니다.
- DirtiesContextBeforeModesTestExecutionListener: "이전" 모드에 대한 @DirtiesContext어노테이션을 처리합니다.
- ApplicationEventsTestExecutionListener:ApplicationEvents에 대한 지원을 제공합니다.
- 의존성 주입 테스트 실행 리스너: 테스트 인스턴스에 대한 종속성 주입을 제공합니다.
- MicrometerObservationRegistryTestExecutionListener: Micrometer의 관찰 레지스트리를 지원합니다.
- DirtiesContextTestExecutionListener: "이후" 모드에 대한 @DirtiesContext 어노테이션을 처리합니다.
- 트랜잭션 테스트 실행 리스너: 기본 롤백 시맨틱으로 트랜잭션 테스트 실행을 제공합니다.
- SqlScriptsTestExecutionListener: SQL어노테이션을 사용하여 구성된 SQL 스크립트를 실행합니다.
- 이벤트 퍼블리싱 테스트 실행 리스너: 테스트 실행 이벤트를 테스트의ApplicationContext에 게시합니다( 테스트 실행 이벤트 참조).
Registering TestExecutionListener Implementations
테스트 클래스, 그 서브클래스 및 중첩 클래스에 대해 @TestExecutionListeners 어노테이션을 사용하여 명시적으로 테스트 클래스, 그 서브클래스 및 중첩 클래스에 대한 TestExecutionListener 구현을 등록할 수 있습니다. 자세한 내용과 예제는어노테이션 지원 및@TestExecutionListeners에대한 자바독을 참조하세요.
기본 테스트 실행 리스너 구현으로 전환하기
테스트 실행 리스너로 어노테이션된 클래스를 확장할 때 기본 리스너 집합을 사용하도록 전환해야 하는 경우 다음과 같이 클래스에 어노테이션을 추가할 수 있습니다.
|
Automatic Discovery of Default TestExecutionListener Implementations
테스트 실행 리스너 구현을 등록하는 방법은 제한된 테스트 시나리오에서 사용되는 사용자 지정 리스너에 적합합니다. 그러나 전체 테스트 스위트에서 사용자 지정 리스너를 사용해야 하는 경우 번거로울 수 있습니다. 이 문제는 SpringFactoriesLoader 메커니즘을 통해 기본TestExecutionListener 구현의 자동 검색을 지원함으로써 해결됩니다.
예를 들어, spring-test 모듈은 META-INF/spring.factories속성 파일의 org.springframework.test.context.TestExecutionListener 키 아래에 모든 핵심 기본 TestExecutionListener구현을 선언합니다. 서드파티 프레임워크와 개발자는 자체 spring.factories 파일을 통해 동일한 방식으로 기본 리스너 목록에 자체TestExecutionListener 구현을 제공할 수 있습니다.
Ordering TestExecutionListener Implementations
테스트 컨텍스트 프레임워크가 앞서 언급한SpringFactoriesLoader 메커니즘을 통해 기본 TestExecutionListener 구현을 발견하면, 인스턴스화된 리스너는 Spring의 Ordered 인터페이스와@Order 주석을 사용하여 순서를 지정하는 Spring의 AnnotationAwareOrderComparator를 통해 정렬됩니다. Spring에서 제공하는 모든 기본TestExecutionListener 구현은 적절한 값으로 Ordered를 구현합니다. 따라서 타사 프레임워크와 개발자는 Ordered를 구현하거나 @Order를 선언하여 기본 TestExecutionListener 구현이 적절한 순서로 등록되었는지 확인해야 합니다. 각 핵심 리스너에 할당되는 값에 대한 자세한 내용은 핵심 기본 TestExecutionListener 구현의 getOrder()메서드에 대한 javadoc을 참조하세요.
Merging TestExecutionListener Implementations
테스트 실행 리스너를 통해 사용자 지정 테스트 실행 리스너를 등록하면 기본 리스너는 등록되지 않습니다. 대부분의 일반적인 테스트 시나리오에서는 개발자가 사용자 지정 리스너와 더불어 모든 기본 리스너를 수동으로 선언해야 합니다. 다음 목록은 이러한 구성 스타일을 보여줍니다:
@ContextConfiguration
@TestExecutionListeners({
MyCustomTestExecutionListener.class,
ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class
})
class MyTest {
// class body...
}
이 접근 방식의 문제점은 개발자가 기본적으로 어떤 리스너가 등록되어 있는지 정확히 알고 있어야 한다는 것입니다. 또한 기본 리스너 세트는 릴리스마다 변경될 수 있습니다(예: SqlScriptsTestExecutionListener는 Spring Framework 4.1에 도입되었고, DirtiesContextBeforeModesTestExecutionListener는Spring Framework 4.2에 도입되었습니다). 또한 Spring Boot 및 Spring Security와 같은 서드파티 프레임워크는 앞서 언급한 자동 검색 메커니즘을사용하여 자체 기본 TestExecutionListener구현을 등록합니다.
모든 기본 리스너를 인식하고 다시 선언할 필요가 없도록 하려면 @TestExecutionListeners의mergeMode 속성을 MergeMode.MERGE_WITH_DEFAULTS로 설정하면 됩니다.MERGE_WITH_DEFAULTS는 로컬로 선언된 리스너가 기본 리스너와 병합되어야 함을 나타냅니다. 병합 알고리즘은 목록에서 중복이 제거되고 병합된 리스너의 결과 집합이 TestExecutionListener 구현 순서 지정에 설명된 대로 AnnotationAwareOrderComparator의 의미에 따라 정렬되도록 합니다. 리스너가 Ordered를 구현하거나 @Order로 주석이 달린 경우 기본값과 병합되는 위치에 영향을 줄 수 있습니다. 그렇지 않으면 병합 시 로컬로 선언된 리스너가 기본 리스너 목록에 추가됩니다.
예를 들어, 이전 예제의 MyCustomTestExecutionListener 클래스가 주문 값(예: 500)을ServletTestExecutionListener의 주문( 1000)보다 작도록 구성한 경우MyCustomTestExecutionListener는 자동으로 ServletTestExecutionListener 앞의 기본 목록과 병합될 수 있으며 이전 예제는 다음과 같이 바뀔 수 있습니다:
@ContextConfiguration
@TestExecutionListeners(
listeners = MyCustomTestExecutionListener.class,
mergeMode = MERGE_WITH_DEFAULTS
)
class MyTest {
// class body...
}
애플리케이션 이벤트
Spring 프레임워크 5.3.3부터 TestContext 프레임워크는 테스트 내에서 해당 이벤트에 대해 어설션을 수행할 수 있도록ApplicationContext에 게시된 애플리케이션이벤트를 기록하는 기능을 지원합니다. 단일 테스트를 실행하는 동안 게시된 모든 이벤트는java.util.Stream으로 이벤트를 처리할 수 있는 ApplicationEvents API를 통해 사용할 수 있습니다.
테스트에서 ApplicationEvents를 사용하려면 다음과 같이 하세요.
- 테스트 클래스에@RecordApplicationEvents로 주석을 달거나 메타 주석을 달았는지 확인합니다.
- ApplicationEventsTestExecutionListener가 등록되어 있는지 확인합니다. 그러나 ApplicationEventsTestExecutionListener는 기본적으로 등록되어 있으며 기본 리스너를 포함하지 않는@TestExecutionListeners를 통한 사용자 지정 구성이 있는 경우에만 수동으로 등록해야 합니다.
- ApplicationEvents 유형의 필드에 @Autowired로 주석을 달고 테스트 및 수명 주기 메서드(예: JUnit Jupiter의 @BeforeEach 및@AfterEach 메서드)에서 해당ApplicationEvents 인스턴스를 사용하세요.
-
- JUnit Jupiter용 SpringExtension을 사용하는 경우, 테스트 클래스에서 @Autowired 필드 대신 테스트 또는 라이프사이클 메서드에 ApplicationEvents 유형의 메서드 매개 변수를 선언할 수 있습니다.
다음 테스트 클래스는 Spring 관리 컴포넌트에서 메서드를 호출하는 동안 게시된 애플리케이션 이벤트 유형을 어설트하기 위해 JUnit Jupiter 및AssertJ용 SpringExtension을 사용합니다:
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents
class OrderServiceTests {
@Autowired
OrderService orderService;
@Autowired
ApplicationEvents events;
@Test
void submitOrder() {
// Invoke method in OrderService that publishes an event
orderService.submitOrder(new Order(/* ... */));
// Verify that an OrderSubmitted event was published
long numEvents = events.stream(OrderSubmitted.class).count();
assertThat(numEvents).isEqualTo(1);
}
}
테스트 클래스에 @RecordApplicationEvents로 주석을 달아요. | |
현재 테스트에 대한 ApplicationEvents 인스턴스를 주입합니다. | |
ApplicationEvents API를 사용하여 게시된 주문 제출 이벤트 수를 계산합니다. |
ApplicationEvents API에 대한 자세한 내용은ApplicationEvents자바독을 참조하세요.
Test Execution Events
Spring 프레임워크 5.2에 도입된 이벤트 퍼블리싱 테스트 실행 리스너는 사용자 정의 테스트 실행 리스너를 구현하는 대안적인 접근 방식을 제공합니다. 테스트의 ApplicationContext에 있는 컴포넌트는EventPublishingTestExecutionListener가 게시하는 다음 이벤트를 수신할 수 있으며, 각 이벤트는TestExecutionListener API의 메서드에 해당합니다.
- BeforeTestClassEvent
- PrepareTestInstanceEvent
- BeforeTestMethodEvent
- BeforeTestExecutionEvent
- AfterTestExecutionEvent
- AfterTestMethodEvent
- AfterTestClassEvent
이러한 이벤트는 모의 빈을 재설정하거나 테스트 실행을 추적하는 등 다양한 이유로 소비될 수 있습니다. 사용자 정의 TestExecutionListener를 구현하는 대신 테스트 실행 이벤트를 소비하는 것의 한 가지 장점은 테스트 실행 이벤트가 테스트 ApplicationContext에 등록된 모든 Spring 빈에서 소비될 수 있으며 이러한 빈은 의존성 주입 및 ApplicationContext의 다른 기능으로부터 직접 이점을 얻을 수 있다는 것입니다. 이와는 대조적으로 TestExecutionListener는 ApplicationContext의 빈이 아닙니다.
이벤트 퍼블리싱 테스트 실행 리스너는 기본적으로 등록되어 있지만, 애플리케이션 컨텍스트가 이미 로드된 경우에만 이벤트를 퍼블리싱합니다. 이렇게 하면ApplicationContext가 불필요하게 또는 너무 일찍 로드되는 것을 방지할 수 있습니다.
따라서 다른 TestExecutionListener에 의해ApplicationContext가 로드될 때까지 BeforeTestClassEvent는 게시되지 않습니다. 예를 들어, 기본 TestExecutionListener 구현 집합이 등록된 경우 특정 테스트 ApplicationContext를 사용하는 첫 번째 테스트 클래스에 대해서는BeforeTestClassEvent가 게시되지 않지만, 후속 테스트 클래스가 실행될 때 컨텍스트가 이미 로드되어 있을 것이므로(@DirtiesContext 또는 최대 크기 퇴거 정책을 통해 컨텍스트가 ContextCache에서 제거되지 않는 한) 동일한 테스트ApplicationContext를 사용하는 동일한 테스트 스위트의 모든 후속 테스트 클래스에 대해서는 BeforeTestClassEvent가 게시됩니다.
모든 테스트 클래스에 대해 항상 BeforeTestClassEvent가 게시되도록 하려면, 이전 테스트 클래스 콜백에 애플리케이션 컨텍스트를로드하는 테스트 실행 리스 너를 등록해야 하며, 해당 테스트 실행 리스 너는 이벤트 퍼블리싱 테스트 실행 리스너보다먼저 등록해야 합니다.
마찬가지로, 지정된 테스트 클래스의 마지막 테스트 메서드 이후 컨텍스트 캐시에서 ApplicationContext를 제거하기 위해 @DirtiesContext를 사용하는 경우 해당 테스트 클래스에 대해 AfterTestClassEvent가게시되지 않습니다.
|
테스트 실행 이벤트를 수신하기 위해 Spring 빈은org.springframework.context.ApplicationListener 인터페이스를 구현하도록 선택할 수 있습니다. 또는 리스너 메서드에 @EventListener를 어노테이션하고 위에 나열된 특정 이벤트 유형 중 하나를 수신하도록 구성할 수 있습니다(어노테이션 기반 이벤트 리스너 참조). 이 접근 방식의 인기로 인해 Spring은 테스트 실행 이벤트 리스너 등록을 단순화하기 위해 다음과 같은 전용@EventListener 어노테이션을 제공합니다. 이러한 어노테이션은 org.springframework.test.context.event.annotation패키지 내에 있습니다.
- beforeTestClass
- @PrepareTestInstance
- @BeforeTestMethod
- @BeforeTestExecution
- afterTestExecution
- @AfterTestMethod
- afterTestClass
Exception Handling
기본적으로 테스트 실행 이벤트 리스너가 이벤트를 소비하는 동안 예외를 발생시키면 해당 예외는 사용 중인 기본 테스트 프레임워크(예: JUnit 또는 TestNG)로 전파됩니다. 예를 들어, BeforeTestMethodEvent를 소비하는 동안 예외가 발생하면 해당 테스트 메서드는 예외의 결과로 실패합니다. 반대로 비동기 테스트 실행 이벤트 리스너가 예외를 던지면 예외가 기본 테스트 프레임워크로 전파되지 않습니다. 비동기 예외 처리에 대한 자세한 내용은 @EventListener에 대한 클래스 수준 자바독을 참조하세요.
Asynchronous Listeners
특정 테스트 실행 이벤트 리스너가 이벤트를 비동기적으로 처리하도록 하려면 Spring의 일반 @Async 지원을사용할 수 있습니다. 자세한 내용은@EventListener에 대한 클래스 수준 자바독을 참조하세요.
컨텍스트 관리
각 테스트 컨 텍스트는 자신이 담당하는 테스트 인스턴스에 대한 컨텍스트 관리 및 캐싱 지원을 제공합니다. 테스트 인스턴스는 구성된 ApplicationContext에 대한 액세스 권한을 자동으로 받지 않습니다. 그러나 테스트 클래스가ApplicationContextAware 인터페이스를 구현하는 경우, 테스트 인스턴스에 ApplicationContext에 대한 참조가 제공됩니다. AbstractJUnit4SpringContextTests와AbstractTestNGSpringContextTests는 ApplicationContextAware를 구현하므로 ApplicationContext에 대한 액세스를 자동으로 제공한다는 점에 유의하세요.
자동화된 애플리케이션 컨텍스트
ApplicationContextAware 인터페이스를 구현하는 대신 다음 예제에서 볼 수 있듯이 필드 또는 설정자 메서드의 @Autowired 주석을 통해 테스트 클래스에 대한 애플리케이션 컨텍스트를 주입할 수 있습니다:
마찬가지로 웹 애플리케이션 컨텍스트를 로드하도록 테스트가 구성된 경우 다음과 같이 웹 애플리케이션 컨텍스트를 테스트에 주입할 수 있습니다:
자동화된 종속성 주입은 기본적으로 구성된DependencyInjectionTestExecutionListener에 의해 제공됩니다( 테스트 픽스처의 종속성 주입 참조).
|
TestContext 프레임워크를 사용하는 테스트 클래스는 애플리케이션 컨텍스트를 구성하기 위해 특정 클래스를 확장하거나 특정 인터페이스를 구현할 필요가 없습니다. 대신 클래스 수준에서 @ContextConfiguration 어노테이션을 선언하면 구성이 이루어집니다. 테스트 클래스에서 애플리케이션 컨텍스트 리소스 위치 또는 컴포넌트 클래스를 명시적으로 선언하지 않은 경우, 구성된 ContextLoader가 기본 위치 또는 기본 구성 클래스에서 컨텍스트를 로드하는 방법을 결정합니다. 컨텍스트 리소스 위치 및 컴포넌트 클래스 외에도 애플리케이션 컨텍스트 초기화기를 통해 애플리케이션 컨텍스트를 구성할 수도 있습니다.
다음 섹션에서는 Spring의 @ContextConfiguration 어노테이션을 사용하여 XML 구성 파일, Groovy 스크립트, 구성 요소 클래스(일반적으로 @Configuration 클래스) 또는 컨텍스트 초기화기를 사용하여 테스트 ApplicationContext를 구성하는 방법을 설명합니다. 또는 고급 사용 사례를 위해 자체 사용자 정의 SmartContextLoader를 구현하고 구성할 수도 있습니다.
XML 리소스를 사용한 컨텍스트 구성
XML 구성 파일을 사용하여 테스트에 대한 ApplicationContext를 로드하려면 테스트 클래스에 @ContextConfiguration 주석을 달고 XML 구성 메타데이터의 리소스 위치가 포함된 배열로 위치 속성을 구성하세요. 일반 경로 또는 상대 경로(예: context.xml)는 테스트 클래스가 정의된 패키지에 상대적인 클래스 경로 리소스로 취급됩니다. 슬래시로 시작하는 경로는 절대 클래스 경로 위치로 취급됩니다(예: /org/example/config.xml). 리소스 URL을 나타내는 경로(예: classpath:, file:,http: 등으로 접두사가 붙은 경로)는 그대로 사용됩니다.
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/app-config.xml" and
// "/test-config.xml" in the root of the classpath
@ContextConfiguration(locations = {"/app-config.xml", "/test-config.xml"})
class MyTest {
// class body...
}
위치 속성을 XML 파일 목록으로 설정합니다. |
컨텍스트 구성은 표준 Java 값 속성을 통해 위치 속성에 대한 별칭을 지원합니다. 따라서 @ContextConfiguration에서 추가 속성을 선언할 필요가 없는 경우, 위치속성 이름의 선언을 생략하고 다음 예시에 설명된 속기 형식을 사용하여 리소스 위치를 선언할 수 있습니다:
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"})
class MyTest {
// class body...
}
위치 속성을 사용하지 않고 XML 파일 지정하기. |
컨텍스트 구성 어노테이션에서 위치 속성과 값 속성을 모두 생략하면 TestContext 프레임워크는 기본 XML 리소스 위치를 감지하려고 시도합니다. 특히 GenericXmlContextLoader와GenericXmlWebContextLoader는 테스트 클래스의 이름을 기준으로 기본 위치를 감지합니다. 클래스의 이름이 com.example.MyTest인 경우 GenericXmlContextLoader는 "classpath:com/example/MyTest-context.xml"에서 애플리케이션 컨텍스트를 로드합니다. 다음 예제는 그 방법을 보여줍니다:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration
class MyTest {
// class body...
}
기본 위치에서 구성 로드하기. |
Groovy 스크립트를 사용한 컨텍스트 구성
Groovy Bean 정의 DSL을 사용하는 Groovy 스크립트를 사용하여 테스트에 대한 ApplicationContext를 로드하려면 @ContextConfiguration으로 테스트 클래스에 주석을 달고 Groovy 스크립트의 리소스 위치를 포함하는 배열로 위치 또는 값속성을 구성할 수 있습니다. Groovy 스크립트의 리소스 조회 시맨틱은XML 구성 파일에 대해 설명한 것과 동일합니다.
Groovy 스크립트 지원 사용 설정하기
Groovy 스크립트를 사용하여 Spring TestContext 프레임워크에서 ApplicationContext를 로드하기 위한 지원은 Groovy가 클래스 경로에 있는 경우 자동으로 활성화됩니다 |
다음 예제는 Groovy 구성 파일을 지정하는 방법을 보여줍니다:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/AppConfig.groovy" and
// "/TestConfig.groovy" in the root of the classpath
@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"})
class MyTest {
// class body...
}
Groovy 구성 파일의 위치 지정하기. |
컨텍스트구성 어노테이션에서 위치와 값 속성을 모두 생략하면 테스트 컨텍스트 프레임워크는 기본 Groovy 스크립트를 감지하려고 합니다. 특히 GenericGroovyXmlContextLoader 및 GenericGroovyXmlWebContextLoader는테스트 클래스 이름에 따라 기본 위치를 감지합니다. 클래스 이름이com.example.MyTest인 경우 Groovy 컨텍스트 로더는"classpath:com/example/MyTestContext.groovy"에서 애플리케이션 컨텍스트를 로드합니다. 다음 예제는 기본값을 사용하는 방법을 보여줍니다:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration
class MyTest {
// class body...
}
기본 위치에서 구성 로드하기. |
XML 구성과 Groovy 스크립트를 동시에 선언하기
ContextConfiguration의 위치 또는 값 속성을 사용하여 XML 구성 파일과 Groovy 스크립트를 동시에 선언할 수 있습니다. 구성된 리소스 위치의 경로가 .xml로 끝나면XmlBeanDefinitionReader를 사용하여 로드됩니다. 그렇지 않으면GroovyBeanDefinitionReader를 사용하여 로드됩니다.
다음 목록은 통합 테스트에서 두 가지를 결합하는 방법을 보여줍니다:
|
컴포넌트 클래스를 사용한 컨텍스트 구성
컴포넌트 클래스를 사용하여 테스트에 대한 ApplicationContext를 로드하려면(Java 기반 컨테이너 구성 참조), @ContextConfiguration으로 테스트 클래스에 주석을 달고 컴포넌트 클래스에 대한 참조가 포함된 배열로 클래스 속성을 구성할 수 있습니다. 다음 예제는 그 방법을 보여줍니다:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})
class MyTest {
// class body...
}
컴포넌트 클래스 지정하기. |
컴포넌트 클래스
"컴포넌트 클래스"라는 용어는 다음 중 하나를 지칭할 수 있습니다:
구성 요소 클래스의 구성 및 의미에 대한 자세한 내용은@Configuration 및@Bean의 자바독을 참조하고, 특히 @Bean Lite 모드에 대한 논의에 주의하세요.
|
컨텍스트 컨피규레이션 어노테이션에서 클래스 속성을 생략하면 테스트 컨텍스트 프레임워크는 기본 구성 클래스의 존재를 감지하려고 시도합니다. 특히 AnnotationConfigContextLoader와 AnnotationConfigWebContextLoader는@Configuration 자바독에 지정된 대로 구성 클래스 구현 요건을 충족하는 테스트 클래스의 모든 정적 중첩 클래스를 감지합니다. 구성 클래스 이름은 임의로 지정된다는 점에 유의하세요. 또한 테스트 클래스는 원하는 경우 둘 이상의 정적 중첩 구성 클래스를 포함할 수 있습니다. 다음 예제에서 OrderServiceTest 클래스는 테스트 클래스에 대한 ApplicationContext를 로드하는 데 자동으로 사용되는 Config라는 정적 중첩 구성 클래스를 선언합니다:
@SpringJUnitConfig
// ApplicationContext will be loaded from the static nested Config class
class OrderServiceTest {
@Configuration
static class Config {
// this bean will be injected into the OrderServiceTest class
@Bean
OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
// set properties, etc.
return orderService;
}
}
@Autowired
OrderService orderService;
@Test
void testOrderService() {
// test the orderService
}
}
중첩된 Config 클래스에서 구성 정보를 로드합니다. |
XML, 그루비 스크립트, 컴포넌트 클래스 혼합하기
XML 구성 파일, Groovy 스크립트 및 컴포넌트 클래스(일반적으로 @Configuration 클래스)를 혼합하여 테스트에 대한ApplicationContext를 구성하는 것이 바람직할 수 있습니다. 예를 들어, 프로덕션 환경에서 XML 구성을 사용하는 경우, 테스트에 대해 특정 Spring 관리 구성 요소를 구성하기 위해 @Configuration 클래스를 사용하거나 그 반대의 경우를 결정할 수 있습니다.
또한 일부 서드파티 프레임워크(예: Spring Boot)는 다양한 유형의 리소스(예: XML 구성 파일, Groovy 스크립트,@Configuration 클래스)에서 동시에 ApplicationContext를 로드할 수 있는 최고 수준의 지원을 제공합니다. 지금까지 Spring 프레임워크는 표준 배포에서 이를 지원하지 않았습니다. 따라서 Spring 프레임워크가 스프링 테스트 모듈에서 제공하는 대부분의 SmartContextLoader 구현은 각 테스트 컨텍스트에 대해 하나의 리소스 유형만 지원합니다. 하지만 그렇다고 해서 둘 다 사용할 수 없다는 의미는 아닙니다. 일반적인 규칙에 대한 한 가지 예외는 GenericGroovyXmlContextLoader와GenericGroovyXmlWebContextLoader가 XML 구성 파일과 Groovy 스크립트를 동시에 지원한다는 것입니다. 또한 타사 프레임워크는 @ContextConfiguration을 통해 위치와 클래스의 선언을 모두 지원하도록 선택할 수 있으며, TestContext 프레임워크의 표준 테스트 지원과 함께 다음과 같은 옵션이 있습니다.
리소스 위치(예: XML 또는 Groovy)와 @Configuration클래스를 사용하여 테스트를 구성하려면 하나를 진입점으로 선택하고 다른 하나를 포함하거나 가져와야 합니다. 예를 들어, XML 또는 Groovy 스크립트에서는 컴포넌트 스캔을 사용하거나 일반 Spring 빈으로 정의하여@Configuration 클래스를 포함할 수 있고, @Configuration 클래스에서는 @ImportResource를 사용하여 XML 구성 파일 또는 Groovy 스크립트를 가져올 수 있습니다. 이 동작은 프로덕션 환경에서 애플리케이션을 구성하는 방법과 의미적으로 동일합니다: 프로덕션 구성에서는 XML 또는 Groovy 리소스 위치 집합 또는 프로덕션 ApplicationContext가 로드되는 @Configuration클래스 집합을 정의하지만 다른 유형의 구성도 자유롭게 포함하거나 임포트할 수 있습니다.
Configuration Configuration with Context Customizers
컨텍스트 커스터마이저는 빈 정의가 컨텍스트에 로드된 후 컨텍스트가 새로 고쳐지기 전에 제공된컨텍스트 커스터마이징을 담당하는 컨텍스트 커스터마이저입니다.
ContextCustomizerFactory는 특정 어노테이션의 존재 여부와 같이 지정된 테스트 클래스에 ContextCustomizer가 필요한지 여부를 결정하는 일부 사용자 정의 논리를 기반으로 ContextCustomizer를 생성할 책임이 있습니다. 팩토리는 컨텍스트 로더가 테스트 클래스에 대한 컨텍스트 구성 속성을 처리한 후 MergedContextConfiguration이 생성되기 전에 호출됩니다.
예를 들어 Spring 프레임워크는 기본적으로 등록되어 있는 다음과 같은 ContextCustomizerFactory구현을 제공합니다:
클래스 경로에 WebSocket 지원이 있고 테스트 클래스 또는 그 둘러싸는 클래스 중 하나가@WebAppConfiguration으로 어노테이션되거나 메타 어노테이션된 경우MockServerContainerContextCustomizer를 생성합니다. MockServerContainerContextCustomizer는 새MockServerContainer를 인스턴스화하여jakarta.websocket.server.ServerContainer라는 어트리뷰트 아래에 ServletContext에 저장합니다.
Registering ContextCustomizerFactory Implementations
테스트 클래스, 그 하위 클래스 및 중첩 클래스에 대해 @ContextCustomizerFactories 어노테이션을 사용하여 명시적으로 ContextCustomizerFactory 구현을 등록할 수 있습니다. 자세한 내용과 예제는어노테이션 지원및@ContextCustomizerFactories에대한 자바독을 참조하세요.
Automatic Discovery of Default ContextCustomizerFactory Implementations
컨텍스트커스터마이저팩토리를 사용하여 컨텍스트커스터마이저팩토리 구현을 등록하는 것은 제한된 테스트 시나리오에서 사용되는 사용자 지정 팩토리에 적합합니다. 그러나 전체 테스트 스위트에서 사용자 지정 팩토리를 사용해야 하는 경우에는 번거로울 수 있습니다. 이 문제는 스프링팩토리로더 메커니즘을 통해 기본컨텍스트커스터마이저팩토리 구현의 자동 검색을 지원함으로써 해결됩니다.
예를 들어, Spring Framework 및 Spring Boot에서 테스트 지원을 구성하는 모듈은META-INF/spring.factories 속성 파일에서org.springframework.test.context.ContextCustomizerFactory 키 아래에 모든 핵심 기본 ContextCustomizerFactory 구현을 선언합니다.스프링 테스트 모듈의 spring.factories 파일은여기에서 볼 수 있습니다. 타사 프레임워크 및 개발자는 자체spring.factories 파일을 통해 동일한 방식으로 기본 팩토리 목록에 자체 ContextCustomizerFactory구현을 기여할 수 있습니다.
Merging ContextCustomizerFactory Implementations
사용자 지정 ContextCustomizerFactory가 @ContextCustomizerFactories를 통해 등록되면 앞서 언급한자동 검색 메커니즘을 사용하여 등록한 기본 팩토리와 병합됩니다.
병합 알고리즘은 병합 시 목록에서 중복이 제거되고 로컬로 선언된 공장이 기본 공장 목록에 추가되도록 합니다.
테스트 클래스, 하위 클래스 및 중첩 클래스의 기본 팩토리를 교체하려면 @ContextCustomizerFactories의 mergeMode 속성을MergeMode.REPLACE_DEFAULTS로 설정하면 됩니다.
|
컨텍스트 이니셜라이저를 사용한 컨텍스트 구성
컨텍스트 이니셜라이저를 사용하여 테스트에 대한 ApplicationContext를 구성하려면 테스트 클래스에 @ContextConfiguration 주석을 달고ApplicationContextInitializer를 구현하는 클래스에 대한 참조를 포함하는 배열로 초기화속성을 구성하세요. 그런 다음 선언된 컨텍스트 이니셜라이저를 사용하여 테스트에 로드되는 ConfigurableApplicationContext를 초기화합니다. 선언된 각 이니셜라이저가 지원하는 구체적인 구성가능 애플리케이션 컨 텍스트 유형은 사용 중인스마트 컨텍스트 로더가 생성한 애플리케이션 컨텍스트 유형(일반적으로 GenericApplicationContext)과 호환되어야 한다는 점에 유의하세요. 또한 이니셜라이저가 호출되는 순서는 Spring의Ordered 인터페이스를 구현하는지 또는 Spring의 @Order 어노테이션 또는 표준@Priority 어노테이션으로 주석이 지정되어 있는지에 따라 달라집니다. 다음 예제는 이니셜라이저를 사용하는 방법을 보여줍니다:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from TestConfig
// and initialized by TestAppCtxInitializer
@ContextConfiguration(
classes = TestConfig.class,
initializers = TestAppCtxInitializer.class)
class MyTest {
// class body...
}
구성 클래스와 이니셜라이저를 사용하여 구성 지정하기. |
컨텍스트 컨피규레이션에서 XML 구성 파일, Groovy 스크립트 또는 구성 요소 클래스의 선언을 완전히 생략하고 대신 XML 파일이나 구성 클래스에서 프로그래밍 방식으로 빈 정의를 로드하는 등 컨텍스트에서 빈 등록을 담당하는ApplicationContextInitializer 클래스만 선언할 수도 있습니다. 다음 예제는 이를 수행하는 방법을 보여줍니다:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be initialized by EntireAppInitializer
// which presumably registers beans in the context
@ContextConfiguration(initializers = EntireAppInitializer.class)
class MyTest {
// class body...
}
이니셜라이저만 사용하여 구성 지정하기. |
컨텍스트 구성 상속
컨텍스트 구성은 리소스 위치 또는 슈퍼클래스에 의해 선언된 컴포넌트 클래스 및 컨텍스트 이니셜라이저를 상속할지 여부를 나타내는 부울 inheritLocations 및 inheritInitializers어트리뷰트를 지원합니다. 두 플래그의 기본값은 모두 true입니다. 즉, 테스트 클래스는 리소스 위치 또는 컴포넌트 클래스와 수퍼클래스가 선언한 컨텍스트 이니셜라이저를 상속합니다. 구체적으로, 테스트 클래스의 리소스 위치 또는 컴포넌트 클래스는 수퍼클래스가 선언한 리소스 위치 또는 주석이 달린 클래스 목록에 추가됩니다. 마찬가지로, 주어진 테스트 클래스의 이니셜라이저는 테스트 슈퍼클래스에 의해 정의된 이니셜라이저 집합에 추가됩니다. 따라서 서브클래스는 리소스 위치, 컴포넌트 클래스 또는 컨텍스트 이니셜라이저를 확장할 수 있는 옵션이 있습니다.
ContextConfiguration의 inheritLocations 또는 inheritInitializers 속성이 false로 설정되면 테스트 클래스의 리소스 위치 또는 컴포넌트 클래스와 컨텍스트 초기화기가 각각 섀도화되고 슈퍼클래스에 의해 정의된 구성을 효과적으로 대체합니다.
Spring 프레임워크 5.3부터 테스트 구성은 중첩 클래스에서 상속될 수도 있습니다. 자세한 내용은 @중첩된 테스트 클래스 구성을 참조하세요 |
XML 리소스 위치를 사용하는 다음 예제에서는ExtendedTest용 ApplicationContext가 base-config.xml과 extended-config.xml에서 순서대로 로드됩니다. 따라서 extended-config.x ml에 정의된 빈은 base-config.xml에 정의된 빈을 재정의(즉, 대체)할 수 있습니다. 다음 예제는 한 클래스가 다른 클래스를 확장하여 자체 구성 파일과 수퍼클래스의 구성 파일을 모두 사용할 수 있는 방법을 보여줍니다:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/base-config.xml"
// in the root of the classpath
@ContextConfiguration("/base-config.xml")
class BaseTest {
// class body...
}
// ApplicationContext will be loaded from "/base-config.xml" and
// "/extended-config.xml" in the root of the classpath
@ContextConfiguration("/extended-config.xml")
class ExtendedTest extends BaseTest {
// class body...
}
수퍼클래스에 정의된 구성 파일. | |
서브클래스에 정의된 구성 파일. |
마찬가지로 컴포넌트 클래스를 사용하는 다음 예제에서는 ExtendedTest용 ApplicationContext가 BaseConfig 및 ExtendedConfig 클래스에서 순서대로 로드됩니다. 따라서 ExtendedConfig에 정의된 빈은 BaseConfig에 정의된 빈을 재정의(즉, 대체)할 수 있습니다. 다음 예제는 한 클래스가 다른 클래스를 확장하여 자체 구성 클래스와 수퍼클래스의 구성 클래스를 모두 사용하는 방법을 보여줍니다:
// ApplicationContext will be loaded from BaseConfig
@SpringJUnitConfig(BaseConfig.class)
class BaseTest {
// class body...
}
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
@SpringJUnitConfig(ExtendedConfig.class)
class ExtendedTest extends BaseTest {
// class body...
}
슈퍼클래스에 정의된 구성 클래스. | |
서브클래스에 정의된 구성 클래스. |
컨텍스트 이니셜라이저를 사용하는 다음 예제에서,ExtendedTest용 ApplicationContext는 BaseInitializer와 ExtendedInitializer를 사용하여 초기화됩니다. 그러나 이니셜라이저가 호출되는 순서는 Spring의 Ordered 인터페이스를 구현하는지 또는 Spring의 @Order 어노테이션 또는 표준 @Priority 어노테이션으로 주석이 지정되어 있는지에 따라 달라진다는 점에 유의하세요. 다음 예제는 한 클래스가 다른 클래스를 확장하고 자체 이니셜라이저와 슈퍼클래스의 이니셜라이저를 모두 사용할 수 있는 방법을 보여줍니다:
// ApplicationContext will be initialized by BaseInitializer
@SpringJUnitConfig(initializers = BaseInitializer.class)
class BaseTest {
// class body...
}
// ApplicationContext will be initialized by BaseInitializer
// and ExtendedInitializer
@SpringJUnitConfig(initializers = ExtendedInitializer.class)
class ExtendedTest extends BaseTest {
// class body...
}
슈퍼클래스에 정의된 이니셜라이저. | |
서브클래스에 정의된 이니셜라이저. |
환경 프로파일을 사용한 컨텍스트 구성
Spring 프레임워크는 환경 및 프로필(일명 "빈 정의 프로필")이라는 개념을 최고 수준으로 지원하며, 다양한 테스트 시나리오에 대해 특정 빈 정의 프로필을 활성화하도록 통합 테스트를 구성할 수 있습니다. 이는 테스트 클래스에 @ActiveProfiles 어노테이션을 추가하고 테스트에 대한 ApplicationContext를 로드할 때 활성화해야 하는 프로필 목록을 제공함으로써 이루어집니다.
SmartContextLoaderSPI의 모든 구현에서 @ActiveProfiles를 사용할 수 있지만, 이전ContextLoader SPI의 구현에서는 @ActiveProfiles가 지원되지 않습니다 |
XML 구성과 @Configuration 클래스가 있는 두 가지 예를 살펴봅시다:
<!-- app-config.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<bean id="transferService"
class="com.bank.service.internal.DefaultTransferService">
<constructor-arg ref="accountRepository"/>
<constructor-arg ref="feePolicy"/>
</bean>
<bean id="accountRepository"
class="com.bank.repository.internal.JdbcAccountRepository">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="feePolicy"
class="com.bank.service.internal.ZeroFeePolicy"/>
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script
location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script
location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
<beans profile="default">
<jdbc:embedded-database id="dataSource">
<jdbc:script
location="classpath:com/bank/config/sql/schema.sql"/>
</jdbc:embedded-database>
</beans>
</beans>
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
TransferServiceTest가 실행되면 클래스 경로의 루트에 있는app-config.xml 구성 파일에서 해당 ApplicationContext가 로드됩니다.App-config.xml을 검사하면 계정 리포지토리 빈이데이터 소스 빈에 대한 종속성을 가지고 있음을 알 수 있습니다. 그러나 dataSource는 최상위 빈으로 정의되어 있지 않습니다. 대신,dataSource는 프로덕션 프로필, 개발 프로필 및 기본 프로필에서 세 번 정의됩니다.
TransferServiceTest에 @ActiveProfiles("dev")로 주석을 달면 Spring TestContext 프레임워크가{"dev"}로 설정된 활성 프로필을 사용하여 ApplicationContext를 로드하도록 지시합니다. 결과적으로 임베디드 데이터베이스가 생성되어 테스트 데이터로 채워지고, 계정 리포지토리 빈은 개발 데이터 소스에 대한 참조로 연결됩니다. 이는 통합 테스트에서 우리가 원하는 것과 같습니다.
때로는 기본 프로필에 빈을 할당하는 것이 유용할 수 있습니다. 기본 프로필 내의 빈은 특별히 활성화된 다른 프로필이 없는 경우에만 포함됩니다. 이를 사용하여 애플리케이션의 기본 상태에서 사용할 "폴백" 빈을 정의할 수 있습니다. 예를 들어, 개발 및 프로덕션 프로필에 대한 데이터 소스를 명시적으로 제공하지만 이 중 어느 것도 활성화되지 않은 경우 인메모리 데이터 소스를 기본값으로 정의할 수 있습니다.
다음 코드 목록은 XML 대신 @Configuration 클래스를 사용하여 동일한 구성 및 통합 테스트를 구현하는 방법을 보여줍니다:
@Configuration
@Profile("dev")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("production")
public class JndiDataConfig {
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
@Configuration
public class TransferServiceConfig {
@Autowired DataSource dataSource;
@Bean
public TransferService transferService() {
return new DefaultTransferService(accountRepository(), feePolicy());
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public FeePolicy feePolicy() {
return new ZeroFeePolicy();
}
}
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
이 변형에서는 XML 구성을 4개의 독립적인@Configuration 클래스로 분할했습니다:
- TransferServiceConfig: 를 사용하여 종속성 주입을 통해 데이터소스를 획득합니다.
- StandaloneDataConfig: 개발자 테스트에 적합한 임베디드 데이터베이스에 대한 데이터소스를 정의합니다.
- JndiDataConfig: 프로덕션 환경의 JNDI에서 검색되는 데이터 소스를 정의합니다.
- DefaultDataConfig: 활성화된 프로필이 없는 경우 기본 임베디드 데이터베이스에 대한 데이터 소스를 정의합니다.
XML 기반 구성 예제에서와 마찬가지로 여전히@ActiveProfiles("dev")로 TransferServiceTest에 주석을 달지만 이번에는 @ContextConfiguration 주석을 사용하여 4개의 구성 클래스를 모두 지정합니다. 테스트 클래스 자체의 본문은 완전히 변경되지 않습니다.
특정 프로젝트 내의 여러 테스트 클래스에서 단일 프로파일 세트가 사용되는 경우가 종종 있습니다. 따라서 @ActiveProfiles어노테이션의 중복 선언을 피하려면 기본 클래스에서 @ActiveProfiles를 한 번 선언하면 하위 클래스가 기본 클래스에서 @ActiveProfiles 구성을 자동으로 상속받습니다. 다음 예제에서는 @ActiveProfiles (및 다른 어노테이션)의 선언이 추상 수퍼클래스인 AbstractIntegrationTest로 이동되었습니다:
Spring 프레임워크 5.3부터 테스트 구성은 중첩 클래스에서 상속될 수도 있습니다. 자세한 내용은 @중첩된 테스트 클래스 구성을 참조하세요 |
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
// "dev" profile inherited from superclass
class TransferServiceTest extends AbstractIntegrationTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
또한 다음 예제에서 볼 수 있듯이@ActiveProfiles는 활성 프로파일의 상속을 비활성화하는 데 사용할 수 있는 inheritProfiles 속성을 지원합니다:
// "dev" profile overridden with "production"
@ActiveProfiles(profiles = "production", inheritProfiles = false)
class ProductionTransferServiceTest extends AbstractIntegrationTest {
// test body
}
또한 선언적으로가 아니라 프로그래밍 방식으로 테스트에 대한 활성 프로필을 확인해야 하는 경우가 있습니다:
- 현재 운영 체제.
- 테스트가 지속적 통합 빌드 서버에서 실행되고 있는지 여부.
- 특정 환경 변수의 존재 여부.
- 사용자 지정 클래스 수준 어노테이션의 존재 여부.
- 기타 우려 사항.
프로그래밍 방식으로 활성 빈 정의 프로필을 확인하려면 사용자 지정 ActiveProfilesResolver를 구현하고 @ActiveProfiles의 해결자속성을 사용하여 등록할 수 있습니다. 자세한 내용은 해당자바독을 참조하세요. 다음 예제에서는 사용자 지정OperatingSystemActiveProfilesResolver를 구현하고 등록하는 방법을 보여 줍니다:
// "dev" profile overridden programmatically via a custom resolver
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver.class,
inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
@Override
public String[] resolve(Class<?> testClass) {
String profile = ...;
// determine the value of profile based on the operating system
return new String[] {profile};
}
}
Context Configuration with Test Property Sources
Spring 프레임워크는 프로퍼티 소스의 계층 구조가 있는 환경이라는 개념을 최고 수준으로 지원하며, 테스트별 프로퍼티 소스를 사용하여 통합 테스트를 구성할 수 있습니다. 구성 클래스에서 사용되는 @PropertySource 어노테이션과 달리 테스트 클래스에서 @TestPropertySource 어노테이션을 선언하여 테스트 속성 파일 또는 인라인 속성에 대한 리소스 위치를 선언할 수 있습니다. 이러한 테스트 속성 소스는 어노테이션 통합 테스트를 위해 로드된 ApplicationContext에 대한환경의 PropertySources 집합에 추가됩니다.
SmartContextLoaderSPI의 모든 구현에서 @TestPropertySource를 사용할 수 있지만, 이전ContextLoader SPI의 구현에서는 @TestPropertySource가 지원되지 않습니다.
SmartContextLoader의 구현은 병합된 테스트 프로퍼티 소스 값에 대한 액세스 권한을MergedContextConfiguration의 getPropertySourceDescriptors() 및 getPropertySourceProperties( ) 메서드를 통해 얻을 수 있습니다.
|
Declaring Test Property Sources
테스트 프로퍼티 파일의 위치 또는 값 속성을 사용하여 테스트 프로퍼티 파일을 구성할 수 있습니다.
기본적으로 "classpath:/com/example/test.properties" 또는"file:///path/to/file.xml"과 같은 기존 및 XML 기반 java.util.Properties 파일 형식이 모두 지원됩니다. Spring Framework 6.1부터는 @TestPropertySource의 factory 속성을 통해 사용자 정의PropertySourceFactory를 구성하여 JSON, YAML 등과 같은 다른 파일 형식을 지원할 수 있습니다.
각 경로는 Spring 리소스로 해석됩니다. 일반 경로(예:"test.properties")는 테스트 클래스가 정의된 패키지에 상대적인 클래스 경로 리소스로 취급됩니다. 슬래시로 시작하는 경로는 절대 클래스 경로 리소스로 취급됩니다(예: "/org/example/test.xml"). URL을 참조하는 경로(예 : classpath:, file: 또는 http: 접두사가 붙은 경로)는 지정된 리소스 프로토콜을 사용하여 로드됩니다.
경로의 속성 자리 표시자(예: ${...})는 환경에 대해 확인됩니다.
Spring 프레임워크 6.1부터 리소스 위치 패턴(예: "classpath*:/config/*.properties")도 지원됩니다.
다음 예제는 테스트 속성 파일을 사용합니다:
@ContextConfiguration
@TestPropertySource("/test.properties")
class MyIntegrationTests {
// class body...
}
절대 경로로 속성 파일 지정하기. |
다음 예와 같이 @TestPropertySource의속성 속성을 사용하여 키-값 쌍의 형태로 인라인 속성을 구성할 수 있습니다. 모든 키-값 쌍은 가장 높은 우선순위를 가진 단일 테스트PropertySource로 둘러싸는 Environment에 추가됩니다.
키-값 쌍에 지원되는 구문은 Java 속성 파일의 항목에 정의된 구문과 동일합니다:
- 키=값
- 키:값
- 키 값
위의 구문 변형과 키와 값 사이의 공백 수에 관계없이 속성을 정의할 수 있지만, 테스트 스위트 내에서 하나의 구문 변형과 일관된 공백을 사용하는 것이 좋습니다(예: 키=값, 키=값 대신 항상 키 = 값 등을 사용하는 것을 고려하세요). 마찬가지로, 텍스트 블록을 사용하여 인라인 속성을 정의하는 경우 테스트 스위트 전체에서 인라인 속성에 일관되게 텍스트 블록을 사용해야 합니다.
그 이유는 사용자가 제공한 정확한 문자열이 컨텍스트 캐시의 키를 결정하는 데 사용되기 때문입니다. 따라서 컨텍스트 캐시의 이점을 활용하려면 인라인 프로퍼티를 일관되게 정의해야 합니다.
|
다음 예제에서는 두 개의 인라인 프로퍼티를 설정합니다:
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port = 4242"})
class MyIntegrationTests {
// class body...
}
문자열 배열을 통해 두 개의 속성을 설정합니다. |
Spring 프레임워크 6.1부터 텍스트 블록을 사용하여 단일 문자열에 여러 개의 인라인 프로퍼티를 정의할 수 있습니다. 다음은 텍스트 블록을 사용하여 두 개의 인라인 프로퍼티를 설정하는 예제입니다:
@ContextConfiguration
@TestPropertySource(properties = """
timezone = GMT
port = 4242
""")
class MyIntegrationTests {
// class body...
}
텍스트 블록을 통해 두 개의 속성을 설정합니다. |
Spring 프레임워크 5.2부터 @TestPropertySource를 반복 가능한 어노테이션으로 사용할 수 있습니다. 즉, 단일 테스트 클래스에서 @TestPropertySource의 여러 선언을 가질 수 있으며, 이후 @TestPropertySource어노테이션의 위치 및 속성이 이전 @TestPropertySource 어노테이션의 위치를 재정의할 수 있습니다.
또한 테스트 클래스에서 각각 메타 주석이 있는 여러 개의 구성된 어노테이션을 선언할 수 있으며, 이러한 모든 @TestPropertySource선언은 테스트 프로퍼티 소스에 기여합니다.
직접 존재하는 @TestPropertySource 어노테이션은 항상 메타 존재하는 @TestPropertySource 어노테이션보다 우선합니다. 즉, 직접 존재하는 @TestPropertySource 어노테이션의 위치 및속성은 메타 어노테이션으로 사용되는 @TestPropertySource 어노테이션의위치 및 속성보다 우선합니다.
|
Default Properties File Detection
TestPropertySource가 빈 어노테이션으로 선언된 경우(즉, 위치 또는 속성 속성에 대한 명시적 값 없이), 어노테이션을 선언한 클래스와 관련된 기본 속성 파일을 검색하려고 시도합니다. 예를 들어 어노테이션이 지정된 테스트 클래스가 com.example.MyTest인 경우 해당 기본 속성 파일은 classpath:com/example/MyTest.properties입니다. 기본값을 감지할 수 없는 경우IllegalStateException이 발생합니다.
Precedence
테스트 프로퍼티는 운영 체제 환경, Java 시스템 프로퍼티 또는 애플리케이션이 @PropertySource를 사용하거나 프로그래밍 방식으로 선언적으로 추가한 프로퍼티 소스에서 정의한 프로퍼티보다 우선순위가 높습니다. 따라서 테스트 프로퍼티를 사용하여 시스템 및 애플리케이션 프로퍼티 소스에서 로드된 프로퍼티를 선택적으로 재정의할 수 있습니다. 또한 인라인 프로퍼티는 리소스 위치에서 로드된 프로퍼티보다 우선순위가 높습니다. 단,@DynamicPropertySource를 통해 등록된 프로퍼티는 @TestPropertySource를 통해 로드된 프로퍼티보다 우선순위가 높다는 점에 유의하세요.
다음 예에서 시간대 및 포트 속성 및"/test.properties "에 정의된 모든 속성은 시스템 및 애플리케이션 속성 소스에 정의된 동일한 이름의 모든 속성보다 우선합니다. 또한 "/test.properties " 파일에 시간대 및 포트 속성에 대한 항목이 정의되어 있으면 properties 속성을 사용하여 선언된 인라인 속성에 의해 재정의됩니다. 다음 예제는 파일과 인라인 모두에서 속성을 지정하는 방법을 보여줍니다:
@ContextConfiguration
@TestPropertySource(
locations = "/test.properties",
properties = {"timezone = GMT", "port = 4242"}
)
class MyIntegrationTests {
// class body...
}
Inheriting and Overriding Test Property Sources
테스트 프로퍼티 소스는 슈퍼클래스가 선언한 프로퍼티 파일 및 인라인 프로퍼티의 리소스 위치를 상속할지 여부를 나타내는 부울 inheritLocations 및 inheritProperties어트리뷰트를 지원합니다. 두 플래그의 기본값은 모두 true입니다. 즉, 테스트 클래스는 모든 수퍼클래스가 선언한 위치와 인라인 프로퍼티를 상속합니다. 구체적으로, 테스트 클래스의 위치와 인라인 프로퍼티는 슈퍼클래스가 선언한 위치와 인라인 프로퍼티에 추가됩니다. 따라서 서브클래스는 위치와 인라인 프로퍼티를 확장할 수 있는 옵션이 있습니다. 나중에 나타나는 프로퍼티는 이전에 나타나는 같은 이름의 프로퍼티를 그림자(즉, 재정의)로 덮어쓴다는 점에 유의하세요. 또한 앞서 언급한 우선순위 규칙은 상속된 테스트 프로퍼티 소스에도 적용됩니다.
테스트 프로퍼티 소스의 inheritLocations 또는 inheritProperties 속성이 false로 설정되어 있으면, 테스트 클래스 섀도에 대한 위치 또는 인라인 프로퍼티가 각각 슈퍼클래스에 의해 정의된 구성을 효과적으로 대체합니다.
Spring 프레임워크 5.3부터 테스트 구성은 중첩 클래스에서 상속될 수도 있습니다. 자세한 내용은 @중첩된 테스트 클래스 구성을 참조하세요 |
다음 예제에서는base.properties 파일만 테스트 속성 소스로 사용하여 BaseTest용 ApplicationContext를 로드합니다. 이와 대조적으로, ExtendedTest용 ApplicationContext는 base.properties 및 extended.properties파일을 테스트 속성 소스 위치로 사용하여 로드됩니다. 다음 예제는 속성 파일을 사용하여 서브클래스와 그 슈퍼클래스 모두에서 속성을 정의하는 방법을 보여줍니다:
@TestPropertySource("base.properties")
@ContextConfiguration
class BaseTest {
// ...
}
@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
// ...
}
다음 예제에서 BaseTest용 ApplicationContext는 inlined key1 속성만 사용하여 로드됩니다. 이와 대조적으로, ExtendedTest용 ApplicationContext는 인라인 key1 및 key2 속성을 사용하여 로드됩니다. 다음 예제는 인라인 프로퍼티를 사용하여 서브클래스와 그 슈퍼클래스 모두에서 프로퍼티를 정의하는 방법을 보여줍니다:
@TestPropertySource(properties = "key1 = value1")
@ContextConfiguration
class BaseTest {
// ...
}
@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
// ...
}
Context Configuration with Dynamic Property Sources
Spring 프레임워크 5.2.5부터 TestContext 프레임워크는 @DynamicPropertySource 어노테이션을 통해 동적프로퍼티를 지원합니다. 이 어노테이션은 통합 테스트를 위해 로드된 애플리케이션 컨텍스트에 대한 환경의PropertySource 집합에 동적 값을 가진 속성을 추가해야 하는 통합 테스트에서 사용할 수 있습니다.
동적 프로퍼티 소스 어노테이션과 그 지원 인프라는 원래테스트 컨테이너 기반 테스트의 프로퍼티를 Spring 통합 테스트에 쉽게 노출할 수 있도록 설계되었습니다. 그러나 이 기능은 테스트의 ApplicationContext 외부에서 라이프사이클이 유지되는 모든 형태의 외부 리소스와 함께 사용할 수도 있습니다.
|
클래스 수준에서 적용되는 @TestPropertySource어노테이션과 달리, @DynamicPropertySource는 이름-값 쌍을 환경에 추가하는 데 사용되는 단일 DynamicPropertyRegistry 인수를 허용하는 정적 메서드에 적용해야 합니다. 값은 동적이며 프로퍼티가 확인될 때만 호출되는 공급자를 통해 제공됩니다. 일반적으로 값을 제공하는 데는 메서드 참조가 사용되며, 다음 예제에서 볼 수 있듯이 Testcontainers 프로젝트를 사용하여 SpringApplicationContext 외부에서 Redis 컨테이너를 관리하는 데 사용됩니다. 관리되는 Redis 컨테이너의 IP 주소와 포트는 redis.host 및redis.port 속성을 통해 테스트의 ApplicationContext 내의 컴포넌트에서 사용할 수 있습니다. 이러한 속성은 Spring의 환경추상화를 통해 액세스하거나, 각각@Value("${redis.host}") 및 @Value("${redis.port}")를 통해 Spring 관리 구성 요소에 직접 주입할 수 있습니다.
기본 클래스에서 @DynamicPropertySource를 사용하는데 하위 클래스 간에 동적 속성이 변경되어 하위 클래스의 테스트가 실패하는 경우, 각 하위 클래스가 올바른 동적 속성을 가진 자체 ApplicationContext를 갖도록 하기 위해 @DirtiesContext로 기본 클래스에 주석을 달아야 할 수 있습니다.
|
@SpringJUnitConfig(/* ... */)
@Testcontainers
class ExampleIntegrationTests {
@Container
static GenericContainer redis =
new GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379);
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("redis.host", redis::getHost);
registry.add("redis.port", redis::getFirstMappedPort);
}
// tests ...
}
Precedence
동적 프로퍼티는 운영 체제의 환경, Java 시스템 프로퍼티 또는 애플리케이션이 선언적으로 @PropertySource를 사용하거나 프로그래밍 방식으로 추가한 프로퍼티 소스에서 로드한 프로퍼티보다 우선순위가 높습니다. 따라서 동적 프로퍼티를 사용하여@TestPropertySource, 시스템 프로퍼티 소스 및 애플리케이션 프로퍼티 소스를 통해 로드된 프로퍼티를 선택적으로 재정의할 수 있습니다.
웹 애플리케이션 컨텍스트 로드
TestContext 프레임워크가 표준 ApplicationContext 대신 WebApplicationContext를 로드하도록 지시하려면, 각 테스트 클래스에@WebAppConfiguration 주석을 달면 됩니다.
테스트 클래스에 @WebAppConfiguration이 있으면 통합 테스트를 위해 웹 애플리케이션 컨텍스트 (WAC)를 로드해야 한다고 테스트 컨텍스트 프레임워크(TCF)에 지시합니다. 백그라운드에서 TCF는 MockServletContext가 생성되어 테스트의 WAC에 제공되도록 합니다. 기본적으로MockServletContext의 기본 리소스 경로는 src/main/webapp로 설정됩니다. 이는 JVM의 루트(일반적으로 프로젝트의 경로)를 기준으로 한 경로로 해석됩니다. Maven 프로젝트에서 웹 애플리케이션의 디렉토리 구조에 익숙하다면src/main/webapp이 WAR의 루트에 대한 기본 위치라는 것을 알고 있을 것입니다. 이 기본값을 재정의해야 하는 경우, @WebAppConfiguration어노테이션에 대체 경로를 제공할 수 있습니다(예: @WebAppConfiguration("src/test/webapp")). 파일 시스템 대신 클래스 경로에서 기본 리소스 경로를 참조하려는 경우 Spring의 classpath: 접두사를 사용할 수 있습니다.
웹 애플리케이션 컨텍스트 구현에 대한 Spring의 테스트 지원은 표준 애플리케이션 컨텍스트 구현에 대한 지원과 동등하다는 점에 유의하세요.WebApplicationContext로 테스트할 때는 @ContextConfiguration을 사용하여 XML 구성 파일, Groovy 스크립트 또는 @Configuration 클래스를 자유롭게 선언할 수 있습니다. 또한 @ActiveProfiles, @TestExecutionListeners, @Sql,@Rollback 등과 같은 다른 테스트 어노테이션을 자유롭게 사용할 수 있습니다.
이 섹션의 나머지 예제에서는 WebApplicationContext를 로드하기 위한 다양한 구성 옵션 중 일부를 보여줍니다. 다음 예시는 구성에 대한 규칙에 대한 TestContext 프레임워크의 지원을 보여줍니다:
@ExtendWith(SpringExtension.class)
// defaults to "file:src/main/webapp"
@WebAppConfiguration
// detects "WacTests-context.xml" in the same package
// or static nested @Configuration classes
@ContextConfiguration
class WacTests {
//...
}
리소스 기본 경로를 지정하지 않고 @WebAppConfiguration으로 테스트 클래스에 주석을 달면 리소스 경로는 사실상 파일:src/main/webapp으로 기본값이 지정됩니다. 마찬가지로, 리소스 위치, 컴포넌트클래스 또는 컨텍스트 이니셜라이저를 지정하지 않고 @ContextConfiguration을 선언하면 Spring은 규칙을 사용하여 구성의 존재를 감지하려고 시도합니다(즉, WacTests 클래스와 동일한 패키지의 WacTests-context.xml 또는 정적으로 중첩된 @Configuration 클래스).
다음 예제는@WebAppConfiguration을 사용하여 리소스 기본 경로를 명시적으로 선언하고 @ContextConfiguration을 사용하여 XML 리소스 위치를 선언하는 방법을 보여줍니다:
@ExtendWith(SpringExtension.class)
// file system resource
@WebAppConfiguration("webapp")
// classpath resource
@ContextConfiguration("/spring/test-servlet-config.xml")
class WacTests {
//...
}
여기서 주목해야 할 중요한 점은 이 두 어노테이션이 있는 경로의 의미가 다르다는 것입니다. 기본적으로 @WebAppConfiguration 리소스 경로는 파일 시스템 기반인 반면, @ContextConfiguration 리소스 위치는 클래스 경로 기반입니다.
다음 예제는 Spring 리소스 접두사를 지정하여 두 어노테이션에 대한 기본 리소스 의미를 재정의할 수 있음을 보여줍니다:
@ExtendWith(SpringExtension.class)
// classpath resource
@WebAppConfiguration("classpath:test-web-resources")
// file system resource
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
class WacTests {
//...
}
이 예제의 주석을 이전 예제와 대조해 보세요.
웹 모의 작업
포괄적인 웹 테스트 지원을 제공하기 위해 TestContext 프레임워크에는 기본적으로 활성화된ServletTestExecutionListener가 있습니다.웹 애플리케이션 컨텍스트에 대해 테스트할 때, 이 테스트 실행 리스너는각 테스트 메서드 전에 Spring Web의 RequestContextHolder를 사용하여 기본 스레드 로컬 상태를 설정하고@WebAppConfiguration으로 구성된 기본 리소스 경로에 따라 MockHttpServletRequest, MockHttpServletResponse 및 ServletWebRequest를 생성합니다. 또한 ServletTestExecutionListener는MockHttpServletResponse와 ServletWebRequest가 테스트 인스턴스에 주입될 수 있도록 하고, 테스트가 완료되면 스레드-로컬 상태를 정리합니다.
테스트를 위해 WebApplicationContext를 로드한 후에는 테스트 픽스처를 설정하거나 웹 컴포넌트를 호출한 후 어설션을 수행하는 등 웹 모의와 상호 작용해야 할 수 있습니다. 다음 예제는 테스트 인스턴스에 자동 연결할 수 있는 모의 모형을 보여줍니다. WebApplicationContext와MockServletContext는 모두 테스트 스위트 전체에 캐시되는 반면, 다른 모의는 ServletTestExecutionListener에 의해 테스트 메서드별로 관리된다는 점에 유의하세요.
@SpringJUnitWebConfig
class WacTests {
@Autowired
WebApplicationContext wac; // cached
@Autowired
MockServletContext servletContext; // cached
@Autowired
MockHttpSession session;
@Autowired
MockHttpServletRequest request;
@Autowired
MockHttpServletResponse response;
@Autowired
ServletWebRequest webRequest;
//...
}
컨텍스트 캐싱
TestContext 프레임워크가 테스트에 대한 애플리케이션 컨 텍스트(또는 웹 애플리케이션 컨텍스트)를 로드하면, 해당 컨텍스트는 캐시되어 동일한 테스트 스위트 내에서 동일한 고유 컨텍스트 구성을 선언하는 모든 후속 테스트에 재사용됩니다. 캐싱의 작동 방식을 이해하려면 "고유"와 "테스트 스위트"의 의미를 이해하는 것이 중요합니다
애플리케이션 컨텍스트는 로드하는 데 사용되는 구성 매개변수의 조합으로 고유하게 식별할 수 있습니다. 따라서 구성 매개변수의 고유한 조합은 컨텍스트가 캐시되는 키를 생성하는 데 사용됩니다. 테스트 컨텍스트 프레임워크는 다음 구성 매개변수를 사용하여 컨텍스트 캐시 키를 빌드합니다:
- 위치 ( @ContextConfiguration에서 가져옴)
- 클래스 ( @ContextConfiguration에서 가져옴)
- contextInitializerClasses ( @ContextConfiguration에서 제공)
- contextCustomizers ( ContextCustomizerFactory에서 가져옴) - 여기에는@DynamicPropertySource 메서드는 물론 @MockBean 및 @SpyBean과 같은 Spring Boot의 테스트 지원의 다양한 기능이 포함됩니다.
- contextLoader ( @ContextConfiguration에서 제공)
- 부모 ( @ContextHierarchy에서 제공)
- activeProfiles ( @ActiveProfiles에서 제공)
- 속성 소스 설명자 ( @TestPropertySource에서 제공)
- 속성소스프로퍼티 ( @테스트프로퍼티소스의 속성)
- resourceBasePath ( @WebAppConfiguration에서 가져옴)
예를 들어 TestClassA가 @ContextConfiguration의위치 (또는 값) 속성에 {"app-config.xml", "test-config.xml"}을 지정하면 TestContext 프레임워크는 해당 ApplicationContext를 로드하고 해당 위치만을 기반으로 하는 키 아래에 있는 정적 컨텍스트 캐시에 저장합니다. 따라서 TestClassB가 명시적으로 또는 상속을 통해 암시적으로 해당 위치에 대해{"app-config.xml", "test-config.xml"}을 정의하지만 @WebAppConfiguration, 다른컨텍스트 로더, 다른 활성 프로필, 다른 컨텍스트 초기화기, 다른 테스트 속성 소스 또는 다른 부모 컨텍스트를 정의하지 않으면 두 테스트 클래스에서 동일한 ApplicationContext를공유하게 됩니다. 즉, 애플리케이션 컨텍스트를 로드하는 데 드는 설정 비용이 테스트 세트당 한 번만 발생하고 후속 테스트 실행이 훨씬 빨라집니다.
테스트 스위트와 포크 프로세스
Spring TestContext 프레임워크는 애플리케이션 컨텍스트를 정적 캐시에 저장합니다. 즉, 컨텍스트는 말 그대로 정적 변수에 저장됩니다. 즉, 테스트가 별도의 프로세스에서 실행되는 경우 각 테스트 실행 사이에 정적 캐시가 지워지므로 캐싱 메커니즘이 효과적으로 비활성화됩니다.
캐싱 메커니즘의 이점을 활용하려면 모든 테스트가 동일한 프로세스 또는 테스트 스위트 내에서 실행되어야 합니다. 이는 IDE 내에서 모든 테스트를 그룹으로 실행하여 달성할 수 있습니다. 마찬가지로 Ant, Maven 또는 Gradle과 같은 빌드 프레임워크로 테스트를 실행할 때는 빌드 프레임워크가 테스트 간에 포크되지 않는지 확인하는 것이 중요합니다. 예를 들어, Maven Surefire 플러그인의forkMode가 항상 또는 pertest로 설정된 경우 TestContext 프레임워크는 테스트 클래스 간에 애플리케이션 컨텍스트를 캐시할 수 없으며, 그 결과 빌드 프로세스가 훨씬 느리게 실행됩니다.
|
컨텍스트 캐시의 크기는 기본 최대 크기인 32로 제한됩니다. 최대 크기에 도달할 때마다 가장 최근에 사용된(LRU) 퇴거 정책이 사용되어 오래된 컨텍스트를 퇴거하고 닫습니다. 명령줄 또는 빌드 스크립트에서 spring.test.context.cache.maxSize라는 JVM 시스템 속성을 설정하여 최대 크기를 구성할 수 있습니다. 또는SpringProperties 메커니즘을 통해 동일한 속성을 설정할 수 있습니다.
지정된 테스트 스위트 내에 많은 수의 애플리케이션 컨텍스트가 로드되면 테스트 스위트를 실행하는 데 불필요하게 오랜 시간이 걸릴 수 있으므로 로드 및 캐시된 컨텍스트 수를 정확히 파악하는 것이 유용할 때가 많습니다. 기본 컨텍스트 캐시에 대한 통계를 보려면org.springframework.test.context.cache 로깅 카테고리의 로그 수준을 DEBUG로 설정하면 됩니다.
드물지만 테스트가 애플리케이션 컨텍스트를 손상시켜 다시 로드해야 하는 경우(예: 빈 정의 또는 애플리케이션 객체의 상태를 수정하여) 테스트 클래스 또는 테스트 메서드에 @DirtiesContext 주석을 추가할 수 있습니다( Spring 테스트 주석의@DirtiesContext에 대한 설명 참조). 이렇게 하면 동일한 애플리케이션 컨텍스트가 필요한 다음 테스트를 실행하기 전에 Spring이 캐시에서 컨텍스트를 제거하고 애플리케이션 컨텍스트를 다시 빌드하도록 지시합니다.DirtiesContext 어노테이션에 대한 지원은 기본적으로 활성화되어 있는DirtiesContextBeforeModesTestExecutionListener 및DirtiesContextTestExecutionListener에 의해 제공됩니다.
애플리케이션 컨텍스트 수명 주기 및 콘솔 로깅
Spring TestContext 프레임워크로 실행된 테스트를 디버깅해야 하는 경우 콘솔 출력(즉, SYSOUT 및 SYSERR스트림에 대한 출력)을 분석하는 것이 유용할 수 있습니다. 일부 빌드 도구와 IDE는 콘솔 출력을 지정된 테스트와 연결할 수 있지만, 일부 콘솔 출력은 지정된 테스트와 쉽게 연결할 수 없습니다.
Spring 프레임워크 자체 또는 ApplicationContext에 등록된 구성 요소에 의해 트리거되는 콘솔 로깅과 관련하여, 테스트 스위트 내에서 Spring TestContext 프레임워크에 의해 로드된ApplicationContext의 라이프사이클을 이해하는 것이 중요합니다.
테스트에 대한 ApplicationContext는 일반적으로 테스트 클래스의 인스턴스가 준비될 때 로드됩니다(예: 테스트 인스턴스의 @Autowired필드에 종속성 주입을 수행하기 위해). 즉, ApplicationContext를 초기화하는 동안 트리거되는 콘솔 로깅은 일반적으로 개별 테스트 메서드와 연결할 수 없습니다. 그러나 @DirtiesContext시맨틱에 따라 테스트 메서드가 실행되기 직전에 컨텍스트가 닫히면 테스트 메서드가 실행되기 직전에 컨텍스트의 새 인스턴스가 로드됩니다. 후자의 시나리오에서는 IDE 또는 빌드 도구가 잠재적으로 콘솔 로깅을 개별 테스트 메서드와 연결할 수 있습니다.
테스트에 대한 ApplicationContext는 다음 시나리오 중 하나를 통해 닫을 수 있습니다.
특정 테스트 메서드 이후에 @DirtiesContext 시맨틱에 따라 컨텍스트가 닫히면 IDE 또는 빌드 도구가 콘솔 로깅을 개별 테스트 메서드와 연결할 가능성이 있습니다. 테스트 클래스 이후 @DirtiesContext 시맨틱에 따라 컨텍스트가 닫히면ApplicationContext가 종료되는 동안 트리거된 콘솔 로깅을 개별 테스트 메서드와 연결할 수 없습니다. 마찬가지로, 종료 단계에서 JVM 종료 훅을 통해 트리거된 콘솔 로깅도 개별 테스트 메서드와 연결할 수 없습니다.
스프링 애플리케이션 컨텍스트가 JVM 종료 훅을 통해 종료되면 종료 단계에서 실행되는 콜백은 스프링 컨텍스트 셧다운 훅이라는 스레드에서 실행됩니다. 따라서 JVM 종료 후크를 통해 ApplicationContext가 종료될 때 트리거되는 콘솔 로깅을 사용하지 않으려면 해당 스레드에서 시작된 모든 로깅을 무시할 수 있는 사용자 정의 필터를 로깅 프레임워크에 등록할 수 있습니다.
|
컨텍스트 실패 임계값
스프링 프레임워크 6.1부터, 실패한 애플리케이션 컨텍스트를 로드하려는 시도가 반복되는 것을 방지하는 컨텍스트 실패 임계값 정책이 적용되었습니다. 기본적으로 실패 임계값은 1로 설정되어 있으며, 이는 주어진 컨텍스트 캐시 키에 대해ApplicationContext를 한 번만 로드하려고 시도한다는 것을 의미합니다(컨텍스트 캐싱 참조). 이후 동일한 컨텍스트 캐시 키에 대해 ApplicationContext를 로드하려고 시도하면 해당 시도가 선제적으로 건너뛰었다는 오류 메시지와 함께 즉각적인 IllegalStateException이 발생합니다. 이 동작을 사용하면 구성 오류 또는 현재 환경에서 컨텍스트를 로드하지 못하게 하는 외부 리소스 누락 등으로 인해 로드에 성공하지 못하는 ApplicationContext를로드하려는 시도가 반복되지 않도록 하여 개별 테스트 클래스 및 테스트 스위트가 더 빨리 실패하지 않도록 할 수 있습니다.
명령줄 또는 빌드 스크립트에서 spring.test.context.failure.threshold라는 이름의 JVM 시스템 속성을 양의 정수 값으로 설정하여 컨텍스트 실패 임계값을 구성할 수 있습니다. 또는SpringProperties 메커니즘을 통해 동일한 속성을 설정할 수도 있습니다.
컨텍스트 실패 임계값을 효과적으로 비활성화하려면 속성을 매우 큰 값으로 설정할 수 있습니다. 예를 들어, 명령줄에서 -Dspring.test.context.failure.threshold=1000000을 통해 시스템 속성을 설정할 수 있습니다 |
컨텍스트 계층
로드된 Spring ApplicationContext에 의존하는 통합 테스트를 작성할 때는 단일 컨텍스트에 대해 테스트하는 것으로 충분할 때가 많습니다. 그러나 ApplicationContext인스턴스의 계층 구조에 대해 테스트하는 것이 유용하거나 심지어 필요한 경우도 있습니다. 예를 들어, Spring MVC 웹 애플리케이션을 개발하는 경우 일반적으로 Spring의 ContextLoaderListener에 의해 로드된 루트 WebApplicationContext와 Spring의 DispatcherServlet에 의해 로드된 자식 WebApplicationContext가 있습니다. 그 결과 공유 구성 요소와 인프라 구성이 루트 컨텍스트에서 선언되고 웹별 구성 요소가 하위 컨텍스트에서 사용하는 부모-자식 컨텍스트 계층 구조가 만들어집니다. 또 다른 사용 사례는 공유 배치 인프라에 대한 구성을 제공하는 부모 컨텍스트와 특정 배치 작업의 구성을 위한 자식 컨텍스트가 있는 Spring 배치 애플리케이션에서 찾을 수 있습니다.
개별 테스트 클래스 또는 테스트 클래스 계층 구조 내에서 @ContextHierarchy 어노테이션으로 컨텍스트 구성을 선언하여 컨텍스트 계층 구조를 사용하는 통합 테스트를 작성할 수 있습니다. 컨텍스트 계층 구조가 테스트 클래스 계층 구조 내의 여러 클래스에 선언된 경우 컨텍스트 계층 구조에서 특정 이름의 레벨에 대한 컨텍스트 구성을 병합하거나 재정의할 수도 있습니다. 계층 구조에서 특정 레벨에 대한 구성을 병합할 때는 구성 리소스 유형(즉, XML 구성 파일 또는 컴포넌트 클래스)이 일관적이어야 합니다. 그렇지 않으면 컨텍스트 계층 구조에서 서로 다른 리소스 유형을 사용하여 서로 다른 레벨을 구성하는 것이 허용됩니다.
이 섹션의 나머지 JUnit Jupiter 기반 예제에서는 컨텍스트 계층 구조를 사용해야 하는 통합 테스트에 대한 일반적인 구성 시나리오를 보여줍니다.
컨텍스트 계층 구조가 있는 단일 테스트 클래스
ControllerIntegrationTests는 두 개의 레벨로 구성된 컨텍스트 계층을 선언하여 Spring MVC 웹 애플리케이션의 일반적인 통합 테스트 시나리오를 나타냅니다. 하나는 루트 WebApplicationContext ( TestAppConfig@Configuration 클래스를 사용하여 로드)와 디스패처 서블릿 WebApplicationContext( WebConfig @Configuration 클래스를 사용하여 로드)에 대한 컨텍스트 계층을 선언합니다. 테스트 인스턴스에 자동 배선되는 WebApplicationContext는하위 컨텍스트(즉, 계층 구조에서 가장 낮은 컨텍스트)에 대한 컨텍스트입니다. 다음 목록은 이 구성 시나리오를 보여줍니다:
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = TestAppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {
@Autowired
WebApplicationContext wac;
// ...
}
암시적 부모 컨텍스트가 있는 클래스 계층 구조
이 예제의 테스트 클래스는 테스트 클래스 계층 구조 내에서 컨텍스트 계층 구조를 정의합니다. AbstractWebTests는 Spring 기반 웹 애플리케이션의 루트WebApplicationContext에 대한 구성을 선언합니다. 그러나AbstractWebTests는 @ContextHierarchy를 선언하지 않는다는 점에 유의하세요. 따라서AbstractWebTests의 서브클래스는 선택적으로 컨텍스트 계층 구조에 참여하거나 @ContextConfiguration에 대한 표준 시맨틱을 따를 수 있습니다. SoapWeb서비스 테스트와RestWeb서비스 테스트는 모두 AbstractWebTests를 확장하고 @ContextHierarchy를 사용하여 컨텍스트 계층 구조를 정의합니다. 그 결과 세 개의 애플리케이션 컨텍스트가 로드되고( @ContextConfiguration의 각 선언마다 하나씩), AbstractWebTests의 구성을 기반으로 로드된 애플리케이션 컨텍스트가 구체적인 하위 클래스에 대해 로드된 각 컨텍스트의 상위 컨텍스트로 설정됩니다. 다음 목록은 이 구성 시나리오를 보여줍니다:
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
public abstract class AbstractWebTests {}
@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml"))
public class SoapWebServiceTests extends AbstractWebTests {}
@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml"))
public class RestWebServiceTests extends AbstractWebTests {}
병합된 컨텍스트 계층 구조 구성이 있는 클래스 계층 구조
이 예제의 클래스는 컨텍스트 계층 구조의 특정 레벨에 대한 구성을 병합하기 위해 명명된 계층 구조 레벨을 사용하는 방법을 보여줍니다. BaseTests는 계층 구조에서 부모와 자식이라는 두 가지 레벨을 정의합니다. ExtendedTests는 BaseTests를 확장하고@ContextConfiguration의 이름 속성에 선언된 이름이 모두 하위인지 확인하여 하위계층 수준에 대한 컨텍스트 구성을 병합하도록 Spring TestContext 프레임워크에 지시합니다. 그 결과 세 개의 애플리케이션 컨텍스트가 로드됩니다. 하나는 /app-config.xml용, 하나는 /user-config.xml용, 하나는{"/user-config.xml", "/order-config.xml"}용입니다. 이전 예제와 마찬가지로 /app-config.xml에서 로드된 애플리케이션 컨텍스트는 /user-config.xml 및 {"/user-config.xml", "/order-config.xml"}에서 로드된 컨텍스트의 부모 컨텍스트로 설정됩니다. 다음 목록은 이 구성 시나리오를 보여 줍니다:
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
@ContextConfiguration(name = "child", locations = "/user-config.xml")
})
class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(name = "child", locations = "/order-config.xml")
)
class ExtendedTests extends BaseTests {}
재정의된 컨텍스트 계층 구조 구성이 있는 클래스 계층 구조
이전 예제와 달리 이 예제에서는 @ContextConfiguration의inheritLocations 플래그를 false로 설정하여 컨텍스트 계층 구조에서 지정된 네임드 레벨에 대한 구성을 재정의하는 방법을 보여줍니다. 결과적으로 ExtendedTests에 대한 애플리케이션 컨텍스트는 /test-user-config.xml에서만 로드되고 부모는 /app-config.xml에서 로드된 컨텍스트로 설정됩니다. 다음 목록은 이 구성 시나리오를 보여줍니다:
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
@ContextConfiguration(name = "child", locations = "/user-config.xml")
})
class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(
name = "child",
locations = "/test-user-config.xml",
inheritLocations = false
))
class ExtendedTests extends BaseTests {}
컨텍스트 계층 구조 내에서 컨텍스트 더티 처리하기
컨텍스트 계층 구조의 일부로 컨텍스트가 구성된 테스트에서 @DirtiesContext를 사용하는 경우 hierarchyMode 플래그를 사용하여 컨텍스트 캐시가 지워지는 방식을 제어할 수 있습니다. 자세한 내용은Spring 테스트 어노테이션의 @DirtiesContext에 대한 설명과@DirtiesContext 자바독을 참조하세요 |
테스트 픽스처의 종속성 주입
DependencyInjectionTestExecutionListener (기본적으로 구성됨)를 사용하는 경우, 테스트 인스턴스의 종속성은 @ContextConfiguration 또는 관련 어노테이션으로 구성한 애플리케이션 컨텍스트의 빈에서 주입됩니다. 선택한 어노테이션과 세터 메서드 또는 필드에 배치할지 여부에 따라 세터 주입, 필드 주입 또는 둘 다를 사용할 수 있습니다. JUnit Jupiter를 사용하는 경우 선택적으로 생성자 주입을 사용할 수도 있습니다( SpringExtension을 사용한 의존성 주입 참조). Spring의 어노테이션 기반 주입 지원과의 일관성을 위해 필드 및 세터 주입을 위해 Spring의 @Autowired 어노테이션 또는 JSR-330의 @Inject어노테이션을 사용할 수도 있습니다.
JUnit Jupiter 이외의 테스트 프레임워크의 경우, TestContext 프레임워크는 테스트 클래스의 인스턴스화에 참여하지 않습니다. 따라서 생성자에 @Autowired 또는@Inject를 사용해도 테스트 클래스에는 영향을 미치지 않습니다 |
프로덕션 코드에서는 필드 주입을 권장하지 않지만 테스트 코드에서는 필드 주입이 매우 자연스럽습니다. 그 차이의 근거는 테스트 클래스를 직접 인스턴스화하지 않기 때문입니다. 따라서 테스트 클래스에서 공용 생성자나 설정자 메서드를 호출할 필요가 없습니다 |
유형별로 자동 배선을수행하는 데 @Autowired가 사용되므로 동일한 유형의 빈 정의가 여러 개 있는 경우 해당 특정 빈에 대해 이 접근 방식을 사용할 수 없습니다. 이 경우 @Autowired를 @Qualifier와 함께 사용할 수 있습니다. 또한 @Inject를@Named와 함께 사용하도록 선택할 수도 있습니다. 또는 테스트 클래스가 해당 ApplicationContext에 액세스할 수 있는 경우, 예를 들어applicationContext.getBean("titleRepository", TitleRepository.class) 호출을 사용하여 명시적 조회를 수행할 수 있습니다.
테스트 인스턴스에 종속성 주입을 적용하지 않으려면 필드 또는 설정자 메서드에 @Autowired 또는 @Inject로 주석을 달지 마세요. 또는@TestExecutionListeners를 사용하여 클래스를 명시적으로 구성하고 리스너 목록에서 DependencyInjectionTestExecutionListener.class를생략하여 종속성 주입을 완전히 비활성화할 수 있습니다.
목표 섹션에 설명된 대로 HibernateTitleRepository 클래스를 테스트하는 시나리오를 고려해 보세요. 다음 두 코드 목록은 필드 및 설정자 메서드에서 @Autowired를 사용하는 방법을 보여줍니다. 애플리케이션 컨텍스트 구성은 모든 샘플 코드 목록 뒤에 제공됩니다.
다음 코드 목록의 종속성 주입 동작은 JUnit Jupiter에만 국한되지 않습니다. 지원되는 모든 테스트 프레임워크와 함께 동일한 DI 기법을 사용할 수 있습니다.
다음 예제에서는 assertNotNull()과 같은 정적 어설션 메서드를 호출하지만 호출 앞에 어설션을 붙이지 않고 호출합니다. 이러한 경우 예제에 표시되지 않은 import 정적 선언을 통해 메서드를 올바르게 임포트했다고 가정합니다.
|
첫 번째 코드 목록은 필드 삽입을 위해 @Autowired를 사용하는 테스트 클래스의 JUnit Jupiter 기반 구현을 보여줍니다:
@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);
}
}
또는 다음과 같이 세터 주입에 @Autowired를 사용하도록 클래스를 구성할 수 있습니다:
@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
HibernateTitleRepository titleRepository;
@Autowired
void setTitleRepository(HibernateTitleRepository titleRepository) {
this.titleRepository = titleRepository;
}
@Test
void findById() {
Title title = titleRepository.findById(new Long(10));
assertNotNull(title);
}
}
앞의 코드 목록은@ContextConfiguration 어노테이션에서 참조하는 동일한 XML 컨텍스트 파일(즉, repository-config.xml)을 사용합니다. 다음은 이 구성을 보여줍니다:
<?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>
설정자 메서드 중 하나에@Autowired를 사용하는 Spring 제공 테스트 베이스 클래스에서 확장하는 경우, 애플리케이션 컨텍스트에 정의된 영향을 받는 유형의 빈(예: 여러 DataSource 빈)이 여러 개 있을 수 있습니다. 이러한 경우 다음과 같이 설정자 메서드를 재정의하고 @Qualifier 어노테이션을 사용하여 특정 대상 빈을 나타낼 수 있습니다(단, 수퍼클래스에서도 재정의된 메서드에 위임해야 합니다):
지정된 한정자 값은 주입할 특정 데이터 소스 빈을 나타내며, 특정 빈에 대한 유형 일치 집합을 좁힙니다. 이 값은 해당 <bean> 정의 내의<qualifier> 선언과 비교하여 일치합니다. 빈 이름은 폴백 한정자 값으로 사용되므로, 앞에서 보여드린 것처럼 이름으로 특정 빈을 효과적으로 가리킬 수도 있습니다( myDataSource가 빈 ID라고 가정할 때).
|
요청 및 세션 범위 빈 테스트하기
Spring은 초기부터 요청 및 세션범위 빈을 지원해 왔으며, 다음 단계에 따라 요청 범위 및 세션 범위 빈을 테스트할 수 있습니다:
- 테스트 클래스에 @WebAppConfiguration 주석을 추가하여 테스트에 WebApplicationContext가 로드되었는지 확인합니다.
- 모의 요청 또는 세션을 테스트 인스턴스에 주입하고 테스트 픽스처를 적절히 준비합니다.
- (종속성 주입을 통해) 구성된WebApplicationContext에서 검색한 웹 컴포넌트를 호출합니다.
- 모의 테스트에 대해 어설션을 수행합니다.
다음 코드 스니펫은 로그인 사용 사례에 대한 XML 구성을 보여줍니다.사용자 서비스 빈은 요청 범위가 지정된 로그인 액션 빈에 대한 종속성을 가지고 있습니다. 또한LoginAction은 현재 HTTP 요청에서 사용자 이름과 비밀번호를 검색하는 SpEL 표현식을 사용하여 인스턴스화됩니다. 테스트에서는 테스트 컨텍스트 프레임워크에서 관리하는 모형을 통해 이러한 요청 매개변수를 구성하려고 합니다. 다음 목록은 이 사용 사례에 대한 구성을 보여줍니다:
<beans>
<bean id="userService" class="com.example.SimpleUserService"
c:loginAction-ref="loginAction"/>
<bean id="loginAction" class="com.example.LoginAction"
c:username="#{request.getParameter('user')}"
c:password="#{request.getParameter('pswd')}"
scope="request">
<aop:scoped-proxy/>
</bean>
</beans>
요청 범위 지정 빈 테스트에서는 UserService (즉, 테스트 대상)와 MockHttpServletRequest를 모두 테스트 인스턴스에 주입합니다.요청 범위() 테스트 메서드 내에서 제공된 MockHttpServletRequest에 요청 매개 변수를 설정하여 테스트 픽스처를 설정합니다.UserService에서 loginUser() 메서드가 호출되면 사용자 서비스가 현재 MockHttpServletRequest (즉, 방금 매개변수를 설정한 것)에 대한 요청 범위의loginAction에 액세스할 수 있다는 것을 확인합니다. 그런 다음 사용자 이름과 비밀번호에 대해 알려진 입력을 기반으로 결과에 대해 어설션을 수행할 수 있습니다. 다음 목록은 이를 수행하는 방법을 보여줍니다:
@SpringJUnitWebConfig
class RequestScopedBeanTests {
@Autowired UserService userService;
@Autowired MockHttpServletRequest request;
@Test
void requestScope() {
request.setParameter("user", "enigma");
request.setParameter("pswd", "$pr!ng");
LoginResults results = userService.loginUser();
// assert results
}
}
다음 코드 조각은 앞서 살펴본 요청 범위 빈에 대한 코드 조각과 유사합니다. 하지만 이번에는 userService 빈이 세션 범위의userPreferences 빈에 대한 종속성을 가집니다. UserPreferences 빈은 현재 HTTP 세션에서 테마를 검색하는 SpEL 표현식을 사용하여 인스턴스화된다는 점에 유의하세요. 테스트에서는 TestContext 프레임워크에서 관리하는 모의 세션에서 테마를 구성해야 합니다. 다음 예제는 그 방법을 보여줍니다:
<beans>
<bean id="userService" class="com.example.SimpleUserService"
c:userPreferences-ref="userPreferences" />
<bean id="userPreferences" class="com.example.UserPreferences"
c:theme="#{session.getAttribute('theme')}"
scope="session">
<aop:scoped-proxy/>
</bean>
</beans>
세션 범위 지정 빈 테스트에서는 UserService와 MockHttpSession을 테스트 인스턴스에 주입합니다. 세션 범위() 테스트 메서드 내에서 제공된 MockHttpSession에서 예상 테마 속성을 설정하여 테스트 픽스처를 설정합니다. UserService에서processUserPreferences() 메서드가 호출되면 사용자 서비스가 현재MockHttpSession에 대한 세션 범위의 userPreferences에 액세스할 수 있으며 구성된 테마에 따라 결과에 대한 어설션을 수행할 수 있습니다. 다음 예제는 이를 수행하는 방법을 보여줍니다:
@SpringJUnitWebConfig
class SessionScopedBeanTests {
@Autowired UserService userService;
@Autowired MockHttpSession session;
@Test
void sessionScope() throws Exception {
session.setAttribute("theme", "blue");
Results results = userService.processUserPreferences();
// assert results
}
}
Transaction Management
테스트 클래스에서 @TestExecutionListeners를 명시적으로 선언하지 않더라도 기본적으로 구성되는트랜잭션은 테스트 컨텍스트 프레임워크에서 트랜잭션이 관리됩니다. 그러나 트랜잭션 지원을 활성화하려면 @ContextConfiguration 시맨틱으로 로드되는ApplicationContext에서 PlatformTransactionManager 빈을 구성해야 합니다(자세한 내용은 나중에 제공됨). 또한 테스트의 클래스 또는 메서드 수준에서 Spring의 @Transactional어노테이션을 선언해야 합니다.
Test-managed Transactions
테스트 관리형 트랜잭션은TransactionalTestExecutionListener를 사용하여 선언적으로 관리하거나 TestTransaction(나중에 설명)을 사용하여 프로그래밍 방식으로 관리되는 트랜잭션입니다. 이러한 트랜잭션을 Spring 관리 트랜잭션(테스트를 위해 로드된 ApplicationContext 내에서 Spring이 직접 관리하는 트랜잭션) 또는 애플리케이션 관리 트랜잭션(테스트에 의해 호출되는 애플리케이션 코드 내에서 프로그래밍 방식으로 관리하는 트랜잭션)과 혼동해서는 안 됩니다. Spring 관리 트랜잭션과 애플리케이션 관리 트랜잭션은 일반적으로 테스트 관리 트랜잭션에 참여합니다. 그러나 Spring 관리형 또는 애플리케이션 관리형 트랜잭션이 필수 또는 지원 이외의 전파 유형으로 구성된 경우에는 주의를 기울여야 합니다(자세한 내용은트랜잭션 전파에 대한 토론 참조).
선제적 시간 초과 및 테스트 관리 트랜잭션
Spring의 테스트 관리 트랜잭션과 함께 테스트 프레임워크에서 어떤 형태의 선제적 시간 초과를 사용할 때는 주의를 기울여야 합니다.
특히 Spring의 테스트 지원은 현재 테스트 메서드가 호출되기 전에 트랜잭션 상태를 현재 스레드에 바인딩합니다( java.lang.ThreadLocal 변수를 통해). 테스트 프레임워크가 선제적 시간 초과를 지원하기 위해 새 스레드에서 현재 테스트 메서드를 호출하는 경우, 현재 테스트 메서드 내에서 수행된 모든 작업은 테스트 관리 트랜잭션 내에서 호출되지 않습니다. 따라서 이러한 작업의 결과는 테스트 관리 트랜잭션으로 롤백되지 않습니다. 반대로, 테스트 관리 트랜잭션이 Spring에 의해 올바르게 롤백되더라도 이러한 작업은 영구 저장소(예: 관계형 데이터베이스)에 커밋됩니다.
이러한 상황이 발생할 수 있는 상황에는 다음이 포함되지만 이에 국한되지는 않습니다.
|
Enabling and Disabling Transactions
트랜잭션으로 테스트 메서드에 주석을 달면 기본적으로 테스트 완료 후 자동으로 롤백되는 트랜잭션 내에서 테스트가 실행됩니다. 테스트 클래스에 @트랜잭션으로 주석을 달면 해당 클래스 계층 구조 내의 각 테스트 메서드가 트랜잭션 내에서 실행됩니다. 트랜잭션 (클래스 또는 메서드 수준에서)으로 어노테이션되지 않은 테스트 메서드는 트랜잭션 내에서 실행되지 않습니다. 트랜잭션은 테스트 수명 주기 메서드(예: JUnit Jupiter의 @BeforeAll, @BeforeEach 등으로 어노테이션된 메서드)에서는 지원되지 않습니다. 또한 @Transactional로 주석을 달았지만 전파 속성이NOT_SUPPORTED 또는 NEVER로 설정된 테스트는 트랜잭션 내에서 실행되지 않습니다.
값 및 트랜잭션 관리자 | yes |
propagation | propagation.NOT_SUPPORTED 및 Propagation.NEVER만 지원됩니다 |
격리 | no |
timeout | no |
읽기 전용 | no |
롤백포 및 롤백포클래스명 | 없음: 대신 TestTransaction.flagForRollback() 사용 |
noRollbackFor 및 noRollbackForClassName | 아니요: 대신 TestTransaction.flagForCommit() 사용 |
메서드 수준 라이프사이클 메서드(예: JUnit Jupiter의@BeforeEach 또는 @AfterEach로 주석이 달린 메서드)는 테스트 관리 트랜잭션 내에서 실행됩니다. 반면, 제품군 수준 및 클래스 수준 수명 주기 메서드(예: JUnit Jupiter의 @BeforeAll 또는 @AfterAll로 주석이 지정된 메서드 및 TestNG의@BeforeSuite, @AfterSuite, @BeforeClass 또는 @AfterClass로 주석이 지정된 메서드)는 테스트 관리 트랜잭션 내에서 실행되지 않습니다.
트랜잭션 내에서 스위트 수준 또는 클래스 수준 수명 주기 메서드에서 코드를 실행해야 하는 경우, 해당 PlatformTransactionManager를 테스트 클래스에 주입한 다음 프로그래밍 방식의 트랜잭션 관리를 위해 트랜잭션템플릿과 함께 사용할 수 있습니다.
|
다음 예시는 최대 절전 모드 기반 사용자 저장소에 대한 통합 테스트를 작성하는 일반적인 시나리오를 보여줍니다:
@SpringJUnitConfig(TestConfig.class)
@Transactional
class HibernateUserRepositoryTests {
@Autowired
HibernateUserRepository repository;
@Autowired
SessionFactory sessionFactory;
JdbcTemplate jdbcTemplate;
@Autowired
void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
void createUser() {
// track initial state in test database:
final int count = countRowsInTable("user");
User user = new User(...);
repository.save(user);
// Manual flush is required to avoid false positive in test
sessionFactory.getCurrentSession().flush();
assertNumUsers(count + 1);
}
private int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
private void assertNumUsers(int expected) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
}
}
트랜잭션 롤백 및 커밋 동작에서 설명한 대로, 데이터베이스에 대한 변경 사항은 TransactionalTestExecutionListener에 의해 자동으로 롤백되므로 createUser() 메서드가 실행된 후 데이터베이스를 정리할 필요가 없습니다.
Transaction Rollback and Commit Behavior
기본적으로 테스트 트랜잭션은 테스트 완료 후 자동으로 롤백되지만, 트랜잭션 커밋 및 롤백 동작은 @Commit 및 @Rollback 어노테이션을 통해 선언적으로 구성할 수 있습니다. 자세한 내용은어노테이션 지원 섹션의 해당 항목을 참조하세요.
Programmatic Transaction Management
테스트 관리 트랜잭션의 정적 메서드를 사용하여 프로그래밍 방식으로 테스트 관리 트랜잭션과 상호 작용할 수 있습니다. 예를 들어 테스트 메서드, 전 메서드 및 후 메서드 내에서 TestTransaction을 사용하여 현재 테스트 관리 트랜잭션을 시작 또는 종료하거나 롤백 또는 커밋을 위해 현재 테스트 관리 트랜잭션을 구성할 수 있습니다. TestTransaction에 대한 지원은TransactionalTestExecutionListener가 활성화될 때마다 자동으로 사용할 수 있습니다.
다음 예제는 TestTransaction의 몇 가지 기능을 보여줍니다. 자세한 내용은 TestTransaction에대한 자바독을 참조하세요.
@ContextConfiguration(classes = TestConfig.class)
public class ProgrammaticTransactionManagementTests extends
AbstractTransactionalJUnit4SpringContextTests {
@Test
public void transactionalTest() {
// assert initial state in test database:
assertNumUsers(2);
deleteFromTables("user");
// changes to the database will be committed!
TestTransaction.flagForCommit();
TestTransaction.end();
assertFalse(TestTransaction.isActive());
assertNumUsers(0);
TestTransaction.start();
// perform other actions against the database that will
// be automatically rolled back after the test completes...
}
protected void assertNumUsers(int expected) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
}
}
Running Code Outside of a Transaction
테스트를 실행하기 전에 초기 데이터베이스 상태를 확인하거나 테스트 실행 후 예상되는 트랜잭션 커밋 동작을 확인하기 위해(테스트가 트랜잭션을 커밋하도록 구성된 경우) 트랜잭션 테스트 메서드 전후에 특정 코드를 실행해야 하는 경우가 있습니다. 트랜잭션 테스트실행 리스너는 정확히 이러한 시나리오를 위해 @BeforeTransaction 및@AfterTransaction 어노테이션을 지원합니다. 테스트 클래스의 모든 void메서드 또는 테스트 인터페이스의 모든 void 기본 메서드에 이러한 어노테이션 중 하나를 어노테이션할 수 있으며, 트랜잭션 테스트 실행 리스너는 트랜잭션 전 메서드 또는 트랜잭션 후 메서드가 적절한 시간에 실행되도록 보장합니다.
일반적으로 @BeforeTransaction과 @AfterTransaction 메서드는 인수를 받지 않아야 합니다.
그러나 Spring 프레임워크 6.1부터 JUnit Jupiter와 함께SpringExtension을사용하는 테스트의 경우 @BeforeTransaction 및 @AfterTransaction 메서드는 선택적으로 SpringExtension과 같은 등록된 JUnit ParameterResolver확장에 의해 해결되는 인수를 허용할 수 있습니다. 즉, 다음 예제에서 볼 수 있듯이테스트의 ApplicationContext에서TestInfo 또는 빈과 같은 JUnit 특정 인수가@BeforeTransaction 및 @AfterTransaction 메서드에 제공될 수 있습니다.
|
트랜잭션 테스트 메서드에 대해 테스트 관리되는 트랜잭션 내에서 모든 비포 메서드(예: JUnit Jupiter의 @BeforeEach로 주석이 달린 메서드)와 모든 애프터 메서드(예: JUnit Jupiter의 @AfterEach로 주석이 달린 메서드)가 실행됩니다.
마찬가지로 @BeforeTransaction 또는 @AfterTransaction으로 주석이 달린 메서드는 트랜잭션 테스트 메서드에 대해서만 실행됩니다.
|
Configuring a Transaction Manager
트랜잭션 테스트 실행 리스너는 테스트에 대한 Spring 애플리케이션 컨텍스트에 플랫폼 트랜잭션 관리자 빈이 정의되어 있을 것으로 예상합니다. 테스트의 ApplicationContext 내에 PlatformTransactionManager의 인스턴스가 여러 개 있는 경우 @Transactional("myTxMgr" ) 또는 @Transactional(transactionManager = "myTxMgr")을 사용하여 한정자를 선언하거나@Configuration 클래스에서 TransactionManagementConfigurer를 구현할 수 있습니다. 테스트의 ApplicationContext에서 트랜잭션 관리자를 조회하는 데 사용되는 알고리즘에 대한 자세한 내용은 TestContextTransactionUtils.retrieveTransactionManager() 에대한 javadoc을 참조하세요.
Demonstration of All Transaction-related Annotations
다음 JUnit Jupiter 기반 예제에서는 모든 트랜잭션 관련 주석을 강조하는 가상의 통합 테스트 시나리오를 보여줍니다. 이 예는 모범 사례를 보여주기 위한 것이 아니라 이러한 어노테이션을 어떻게 사용할 수 있는지 보여주기 위한 것입니다. 자세한 정보와 구성 예제는 어노테이션 지원 섹션을 참조하세요. SQL에 대한 트랜잭션 관리에는기본 트랜잭션 롤백 의미론으로 선언적 SQL 스크립트 실행을 위해 @Sql을 사용하는 추가 예가 포함되어 있습니다. 다음 예에서는 관련 주석을 보여줍니다:
@SpringJUnitConfig
@Transactional(transactionManager = "txMgr")
@Commit
class FictitiousTransactionalTest {
@BeforeTransaction
void verifyInitialDatabaseState() {
// logic to verify the initial state before a transaction is started
}
@BeforeEach
void setUpTestDataWithinTransaction() {
// set up test data within the transaction
}
@Test
// overrides the class-level @Commit setting
@Rollback
void modifyDatabaseWithinTransaction() {
// logic which uses the test data and modifies database state
}
@AfterEach
void tearDownWithinTransaction() {
// run "tear down" logic within the transaction
}
@AfterTransaction
void verifyFinalDatabaseState() {
// logic to verify the final state after transaction has rolled back
}
}
ORM 코드를 테스트할 때 오탐 방지
최대 절전 모드 세션 또는 JPA 지속성 컨텍스트의 상태를 조작하는 애플리케이션 코드를 테스트할 때는 해당 코드를 실행하는 테스트 메서드 내에서 기본 작업 단위를 플러시해야 합니다. 기본 작업 단위를 플러시하지 않으면 오탐이 발생할 수 있습니다: 테스트는 통과했지만 동일한 코드가 라이브 프로덕션 환경에서 예외를 발생시키는 경우입니다. 이는 인메모리 작업 단위를 유지하는 모든 ORM 프레임워크에 적용된다는 점에 유의하세요. 다음 최대 절전 모드 기반 예제 테스트 케이스에서 한 메서드는 오탐을 표시하고 다른 메서드는 세션 플러시 결과를 올바르게 노출합니다:
다음 예제는 JPA에 대한 일치하는 메서드를 보여줍니다:
|
ORM 엔티티 수명 주기 콜백 테스트하기
ORM 코드를 테스트할 때 오탐을피하기 위한 참고 사항과 마찬가지로, 애플리케이션에서 엔티티 수명 주기 콜백(엔티티 리스너라고도 함)을 사용하는 경우 해당 코드를 실행하는 테스트 메서드 내에서 기본 작업 단위를 플러시해야 합니다. 기본 작업 단위를 플러시하지 않거나 지우지 않으면 특정 수명 주기 콜백이 호출되지 않을 수 있습니다.
예를 들어 JPA를 사용하는 경우 엔티티가 저장되거나 업데이트된 후 entityManager.flush() 가 호출되지 않으면 @PostPersist, @PreUpdate 및 @PostUpdate 콜백이 호출되지 않습니다. 마찬가지로 엔티티가 이미 현재 작업 단위(현재 지속성 컨텍스트와 연결된)에 연결된 경우 엔티티를 다시 로드하려고 시도하기 전에 entityManager.clear() 가 호출되지 않는 한 @PostLoad 콜백이 호출되지 않습니다.
다음 예제는 엔티티가 지속될 때@PostPersist 콜백이 호출되도록 엔티티 매니저를 플러시하는 방법을 보여줍니다. 이 예제에서 사용된 Person 엔티티에 대해 @PostPersist 콜백 메서드가 있는 엔티티 리스너가 등록되어 있습니다.
모든 JPA 라이프사이클 콜백을 사용하는 작업 예제는 Spring 프레임워크 테스트 스위트의JpaEntityListenerTests를참조하세요.
|
Executing SQL Scripts
관계형 데이터베이스에 대한 통합 테스트를 작성할 때 SQL 스크립트를 실행하여 데이터베이스 스키마를 수정하거나 테스트 데이터를 테이블에 삽입하는 것이 유용할 때가 많습니다.Spring-jdbc 모듈은 Spring ApplicationContext가 로드될 때 SQL 스크립트를 실행하여 임베디드 또는 기존 데이터베이스를 초기화하는 기능을 지원합니다. 자세한 내용은 임베디드 데이터베이스지원 및임베디드 데이터베이스로 데이터 액세스 로직 테스트하기를참조하세요.
애플리케이션 컨텍스트가 로드될 때 테스트를 위해 데이터베이스를 한 번 초기화하는 것이 매우 유용하지만, 통합 테스트 중에 데이터베이스를 수정할 수 있어야 하는 경우도 있습니다. 다음 섹션에서는 통합 테스트 중에 SQL 스크립트를 프로그래밍 방식으로 선언적으로 실행하는 방법을 설명합니다.
Executing SQL scripts programmatically
Spring은 통합 테스트 메서드 내에서 프로그래밍 방식으로 SQL 스크립트를 실행하기 위한 다음과 같은 옵션을 제공합니다.
- org.springframework.jdbc.datasource.init.ScriptUtils
- org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
- org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
- org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests
ScriptUtils는 SQL 스크립트 작업을 위한 정적 유틸리티 메서드 모음을 제공하며 주로 프레임워크 내에서 내부적으로 사용하기 위한 것입니다. 그러나 SQL 스크립트를 구문 분석하고 실행하는 방법을 완전히 제어해야 하는 경우에는 나중에 설명하는 다른 대안보다 ScriptUtils가 더 적합할 수 있습니다. 자세한 내용은 ScriptUtils의 개별 메서드에 대한javadoc을 참조하세요.
ResourceDatabasePopulator는 외부 리소스에 정의된 SQL 스크립트를 사용하여 데이터베이스를 프로그래밍 방식으로 채우고, 초기화하거나, 정리하기 위한 객체 기반 API를 제공합니다. ResourceDatabasePopulator는 스크립트를 구문 분석하고 실행할 때 사용되는 문자 인코딩, 문 구분 기호, 주석 구분 기호 및 오류 처리 플래그를 구성하기 위한 옵션을 제공합니다. 각 구성 옵션에는 적절한 기본값이 있습니다. 기본값에 대한 자세한 내용은javadoc을 참조하세요.ResourceDatabasePopulator에 구성된 스크립트를 실행하려면 populate(Connection) 메서드를 호출하여 java.sql.Connection에 대해 포퓰레이터를 실행하거나 execute(DataSource ) 메서드를 호출하여 javax.sql.DataSource에 대해 포퓰레이터를 실행할 수 있습니다. 다음 예제에서는 테스트 스키마 및 테스트 데이터에 대한 SQL 스크립트를 지정하고, 문 구분 기호를@@로 설정한 다음, DataSource에 대해 스크립트를 실행합니다:
@Test
void databaseTest() {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource("test-schema.sql"),
new ClassPathResource("test-data.sql"));
populator.setSeparator("@@");
populator.execute(this.dataSource);
// run code that uses the test schema and data
}
ResourceDatabasePopulator는 내부적으로 SQL 스크립트 구문 분석 및 실행을 ScriptUtils에 위임한다는 점에 유의하세요. 마찬가지로,AbstractTransactionalJUnit4SpringContextTests및 AbstractTransactionalTestNGSpringContextTests의 executeSqlScript(...) 메서드는 내부적으로 ResourceDatabasePopulator를 사용하여 SQL 스크립트를 실행합니다. 자세한 내용은 다양한 실행SqlScript(..) 메서드에 대한 Javadoc을 참조하세요.
Executing SQL scripts declaratively with @Sql
앞서 언급한 SQL 스크립트를 프로그래밍 방식으로 실행하는 메커니즘 외에도 Spring TestContext 프레임워크에서 SQL 스크립트를 선언적으로 구성할 수 있습니다. 구체적으로, 테스트 클래스 또는 테스트 메서드에 @Sql 주석을 선언하여 통합 테스트 클래스 또는 테스트 메서드 전후에 지정된 데이터베이스에 대해 실행되어야 하는 개별 SQL 문 또는 SQL 스크립트의 리소스 경로를 구성할 수 있습니다. 기본적으로 활성화된 SqlScriptsTestExecutionListener에서 @Sql에 대한 지원을 제공합니다.
메서드 수준의 @Sql 선언은 기본적으로 클래스 수준의 선언을 재정의하지만, 이 동작은 @SqlMergeMode를 통해 테스트 클래스 또는 테스트 메서드별로 구성할 수 있습니다. 자세한 내용은 @SqlMergeMode로 병합 및 재정의 구성을참조하세요.
그러나 이는BEFORE_TEST_CLASS 또는 AFTER_TEST_CLASS 실행 단계에 대해 구성된 클래스 수준 선언에는 적용되지 않습니다. 이러한 선언은 재정의할 수 없으며 해당 스크립트 및 문은 메서드 수준 스크립트 및 문과 함께 클래스당 한 번씩 실행됩니다.
|
경로 리소스 의미론
각 경로는 Spring 리소스로 해석됩니다. 일반 경로(예:"schema.sql")는 테스트 클래스가 정의된 패키지에 상대적인 클래스 경로 리소스로 취급됩니다. 슬래시로 시작하는 경로는 절대 클래스 경로 리소스로 취급됩니다(예: "/org/example/schema.sql"). URL을 참조하는 경로(예 : classpath:, file:, http: 접두사가 붙은 경로)는 지정된 리소스 프로토콜을 사용하여 로드됩니다.
다음 예는 클래스 수준과 JUnit Jupiter 기반 통합 테스트 클래스 내의 메서드 수준에서 @Sql을 사용하는 방법을 보여줍니다:
@SpringJUnitConfig
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
void emptySchemaTest() {
// run code that uses the test schema without any test data
}
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
void userTest() {
// run code that uses the test schema and test data
}
}
기본 스크립트 감지
SQL 스크립트나 문이 지정되지 않은 경우, @Sql이 선언된 위치에 따라 기본스크립트를 감지하려고 시도합니다. 기본 스크립트를 감지할 수 없는 경우IllegalStateException이 발생합니다.
- 클래스 수준 선언: 주석이 달린 테스트 클래스가 com.example.MyTest인 경우, 해당 기본 스크립트는 classpath:com/example/MyTest.sql입니다.
- 메서드 수준 선언: 주석이 달린 테스트 메서드의 이름이 testMethod() 이고 com.example.MyTest 클래스에 정의되어 있는 경우, 해당 기본 스크립트는classpath:com/example/MyTest.testMethod.sql입니다.
SQL 스크립트 및 문 로깅
어떤 SQL 스크립트가 실행되고 있는지 확인하려면org.springframework.test.context.jdbc 로깅 범주를 DEBUG로 설정합니다.
어떤 SQL 문이 실행되고 있는지 확인하려면org.springframework.jdbc.datasource.init 로깅 범주를 DEBUG로 설정합니다.
여러 @Sql 집합 선언하기
주어진 테스트 클래스 또는 테스트 메서드에 대해 여러 SQL 스크립트 집합을 구성해야 하지만 집합마다 다른 구문 구성, 다른 오류 처리 규칙 또는 다른 실행 단계를 사용해야 하는 경우 @Sql의 여러 인스턴스를 선언할 수 있습니다. 반복 가능한 어노테이션으로 @Sql을 사용하거나 @Sql의 여러 인스턴스를 선언하기 위한 명시적 컨테이너로 @SqlGroup 어노테이션을 사용할 수 있습니다.
다음 예는 @Sql을 반복 가능한 어노테이션으로 사용하는 방법을 보여줍니다:
@Test
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`"))
@Sql("/test-user-data.sql")
void userTest() {
// run code that uses the test schema and test data
}
앞의 예제에서 제시된 시나리오에서 test-schema.sql 스크립트는 한 줄 주석에 대해 다른 구문을 사용합니다.
다음 예는 앞의 예와 동일하지만 @Sql선언이 @SqlGroup 내에 함께 그룹화되어 있다는 점이 다릅니다. SqlGroup의 사용은 선택 사항이지만 다른 JVM 언어와의 호환성을 위해 @SqlGroup을 사용해야 할 수도 있습니다.
@Test
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
void userTest() {
// run code that uses the test schema and test data
}
스크립트 실행 단계
기본적으로 SQL 스크립트는 해당 테스트 메서드보다 먼저 실행됩니다. 그러나 테스트 메서드 이후에 특정 스크립트 집합을 실행해야 하는 경우(예: 데이터베이스 상태를 정리하기 위해) 다음 예제와 같이 @Sql의 실행 단계 속성을AFTER_TEST_METHOD로 설정할 수 있습니다:
@Test
@Sql(
scripts = "create-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED)
)
@Sql(
scripts = "delete-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD
)
void userTest() {
// run code that needs the test data to be committed
// to the database outside of the test's transaction
}
ISOLATED 및 AFTER_TEST_METHOD는 각각Sql.TransactionMode 및 Sql.ExecutionPhase에서 정적으로 가져옵니다 |
Spring 프레임워크 6.1부터는 다음 예제에서 볼 수 있듯이 클래스 수준 @Sql선언에서 실행 단계 속성을 BEFORE_TEST_CLASS 또는 AFTER_TEST_CLASS로 설정하여 테스트 클래스 이전 또는 이후에 특정 스크립트 집합을 실행할 수 있습니다:
@SpringJUnitConfig
@Sql(scripts = "/test-schema.sql", executionPhase = BEFORE_TEST_CLASS)
class DatabaseTests {
@Test
void emptySchemaTest() {
// run code that uses the test schema without any test data
}
@Test
@Sql("/test-user-data.sql")
void userTest() {
// run code that uses the test schema and test data
}
}
BEFORE_TEST_CLASS는 Sql.ExecutionPhase에서 정적으로 가져옵니다 |
SqlConfig를 사용한 스크립트 구성
통합 테스트 클래스에서 클래스 수준 어노테이션으로 선언하면 @SqlConfig 어노테이션을 사용하여 스크립트 구문 분석 및 오류 처리를 구성할 수 있습니다. 테스트 클래스 계층 구조 내의 모든 SQL 스크립트에 대한 전역 구성으로 @SqlConfig가사용됩니다. SQL 어노테이션의 config 속성을 사용하여 직접 선언한 경우 @SqlConfig는둘러싸는 @Sql어노테이션 내에 선언된 SQL 스크립트에 대한 로컬 구성으로 사용됩니다. SQLConfig의 모든 어트리뷰트에는 암시적 기본값이 있으며, 이는 해당 어트리뷰트의 자바독에 문서화되어 있습니다. Java 언어 사양의 어노테이션 속성에 정의된 규칙으로 인해 안타깝게도 어노테이션 속성에 null 값을 할당할 수 없습니다. 따라서 상속된 전역 구성의 재정의 지원을 위해 @SqlConfig 속성의 명시적 기본값은 "" (문자열의 경우), {} (배열의 경우) 또는 DEFAULT (열거형의 경우) 중 하나입니다. 이 접근 방식을 사용하면 @SqlConfig의 로컬 선언에서 "", {} 또는 DEFAULT 이외의 값을 제공하여 @SqlConfig의 전역 선언에서 개별 속성을 선택적으로 재정의할 수 있습니다. 로컬 @SqlConfig 속성이 "", {} 또는DEFAULT 이외의 명시적 값을 제공하지 않을 때마다 글로벌 @SqlConfig 속성이 상속됩니다. 따라서 명시적 로컬 구성은 전역 구성을 재정의합니다.
SQL 및 @SqlConfig에서 제공하는 구성 옵션은 ScriptUtils 및 ResourceDatabasePopulator에서 지원하는 것과 동일하지만 <jdbc:initialize-database/> XML 네임스페이스 요소에서 제공하는 것의 상위 집합입니다. 자세한 내용은 @Sql 및@SqlConfig의 개별 속성에 대한 javadoc을 참조하세요.
Sql에 대한 트랜잭션 관리
기본적으로 SqlScriptsTestExecutionListener는 @Sql을 사용하여 구성된 스크립트에 대해 원하는 트랜잭션 시맨틱을 추론합니다. 구체적으로, SQL 스크립트는 트랜잭션 없이, 기존 Spring 관리 트랜잭션(예:@Transactional로 주석을 단 테스트에 대해 TransactionalTestExecutionListener가 관리하는 트랜잭션) 내에서 또는 @SqlConfig의 transactionMode 속성의 구성된 값과 테스트의 ApplicationContext에PlatformTransactionManager가 있는지에 따라 격리된 트랜잭션 내에서 실행됩니다. 그러나 최소한 테스트의 ApplicationContext에 javax.sql.DataSource가 있어야 합니다.
데이터 소스 및플랫폼 트랜잭션 관리자를 감지하고 트랜잭션 시맨틱을 추론하기 위해 SqlScriptsTestExecutionListener가 사용하는 알고리즘이 사용자의 요구에 적합하지 않은 경우 @SqlConfig의 데이터 소스 및 트랜잭션 관리자속성을 설정하여 명시적인 이름을 지정할 수 있습니다. 또한, 스크립트를 격리된 트랜잭션에서 실행할지 여부와 같은 트랜잭션 전파 동작을 제어하기 위해 @SqlConfig의 transactionMode 속성을 설정할 수 있습니다(예: 스크립트를 격리된 트랜잭션에서 실행해야 하는지 여부). SQL로 트랜잭션 관리를 위해 지원되는 모든 옵션에 대해 자세히 설명하는 것은 이 참조 설명서의 범위를 벗어나지만,@SqlConfig 및SqlScriptsTestExecutionListener에대한 javadoc에서 자세한 정보를 제공하며, 다음 예에서는 JUnit Jupiter와 트랜잭션 테스트를 사용하는 일반적인 테스트 시나리오와 @Sql을 사용한 트랜잭션 테스트를 보여줍니다:
@SpringJUnitConfig(TestDatabaseConfig.class)
@Transactional
class TransactionalSqlScriptsTests {
final JdbcTemplate jdbcTemplate;
@Autowired
TransactionalSqlScriptsTests(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
@Sql("/test-data.sql")
void usersTest() {
// verify state in test database:
assertNumUsers(2);
// run code that uses the test data...
}
int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
void assertNumUsers(int expected) {
assertEquals(expected, countRowsInTable("user"),
"Number of rows in the [user] table.");
}
}
데이터베이스에 대한 모든 변경 사항(테스트 메서드 내 또는/test-data.sql 스크립트 내)은TransactionalTestExecutionListener에 의해 자동으로 롤백되므로 usersTest() 메서드가 실행된 후 데이터베이스를 정리할 필요가 없습니다(자세한 내용은 트랜잭션 관리를 참조하세요).
SQLMergeMode로 구성 병합 및 재정의하기
Spring 프레임워크 5.2부터 메서드 수준의 @Sql 선언을 클래스 수준의 선언과 병합할 수 있습니다. 예를 들어, 이를 통해 테스트 클래스당 데이터베이스 스키마 또는 일부 공통 테스트 데이터에 대한 구성을 한 번 제공한 다음 테스트 메서드당 사용 사례별 테스트 데이터를 추가로 제공할 수 있습니다. SQL 병합을 사용하려면 테스트 클래스 또는 테스트 메서드에 @SqlMergeMode(MERGE)로 주석을 달면 됩니다. 특정 테스트 메서드(또는 특정 테스트 하위 클래스)에 대해 병합을 비활성화하려면 @SqlMergeMode(OVERRIDE)를 통해 기본 모드로 다시 전환할 수 있습니다. 예제 및 자세한 내용은 @SqlMergeMode 어노테이션 문서 섹션을참조하세요.
병렬 테스트 실행
스프링 프레임워크 5.0은 스프링 테스트컨텍스트 프레임워크를 사용할 때 단일 JVM 내에서 테스트를 병렬로 실행하는 기본 지원을 도입했습니다. 이는 일반적으로 테스트 코드나 구성을 변경하지 않고도 대부분의 테스트 클래스 또는 테스트 메서드를 병렬로 실행할 수 있음을 의미합니다.
병렬 테스트 실행을 설정하는 방법에 대한 자세한 내용은 테스트 프레임워크, 빌드 도구 또는 IDE의 설명서를 참조하세요 |
테스트 스위트에 동시성을 도입하면 예기치 않은 부작용, 이상한 런타임 동작, 간헐적으로 또는 무작위로 실패하는 테스트가 발생할 수 있다는 점에 유의하세요. 따라서 Spring 팀은 테스트를 병렬로 실행하지 말아야 할 경우에 대한 일반적인 가이드라인을 다음과 같이 제공합니다.
테스트가 다음과 같은 경우에는 테스트를 병렬로 실행하지 마세요:
- Spring 프레임워크의 @DirtiesContext 지원을 사용하세요.
- Spring Boot의 @MockBean 또는 @SpyBean 지원을 사용합니다.
- 테스트 메서드가 특정 순서로 실행되도록 설계된 JUnit 4의 @FixMethodOrder 지원 또는 테스트 프레임워크 기능을 사용하세요. 단, 전체 테스트 클래스가 병렬로 실행되는 경우에는 적용되지 않는다는 점에 유의하세요.
- 데이터베이스, 메시지 브로커, 파일 시스템 등과 같은 공유 서비스 또는 시스템의 상태를 변경합니다. 이는 임베디드 시스템과 외부 시스템 모두에 적용됩니다.
병렬 테스트 실행이 실패하고 현재 테스트에 대한 ApplicationContext가더 이상 활성화되지 않았다는 예외가 발생하면 일반적으로 다른 스레드의 ContextCache에서ApplicationContext가 제거되었음을 의미합니다.
이는 @DirtiesContext를 사용했기 때문이거나ContextCache에서 자동으로 제거되었기 때문일 수 있습니다. DirtiesContext가 원인인 경우 @DirtiesContext를 사용하지 않는 방법을 찾거나 해당 테스트를 병렬 실행에서 제외해야 합니다. 컨텍스트 캐 시의 최대 크기를 초과한 경우 캐시의 최대 크기를 늘릴 수 있습니다. 자세한 내용은 컨텍스트 캐싱에대한 토론을 참조하세요.
|
Spring TestContext 프레임워크에서 병렬 테스트 실행은 TestContext에 대한 자바독에 설명된 대로 기본 TestContext 구현이 복사 생성자를 제공하는 경우에만 가능합니다. Spring에서 사용되는DefaultTestContext는 이러한 생성자를 제공합니다. 그러나 사용자 지정 TestContext 구현을 제공하는 타사 라이브러리를 사용하는 경우 병렬 테스트 실행에 적합한지 확인해야 합니다 |
TestContext Framework Support Classes
이 섹션에서는 스프링 테스트 컨텍스트 프레임워크를 지원하는 다양한 클래스에 대해 설명합니다.
Spring JUnit 4 Runner
Spring 테스트 컨텍스트 프레임워크는 사용자 정의 런너를 통해 JUnit 4와의 완전한 통합을 제공합니다(JUnit 4.12 이상에서 지원). 개발자는 테스트 클래스에@RunWith(SpringJUnit4ClassRunner.class ) 또는 더 짧은 @RunWith(SpringRunner.class)변형으로 주석을 달아 표준 JUnit 4 기반 단위 및 통합 테스트를 구현하는 동시에 애플리케이션 컨텍스트 로드 지원, 테스트 인스턴스의 종속성 주입, 트랜잭션 테스트 방법 실행 등과 같은 TestContext 프레임워크의 이점을 누릴 수 있습니다. 대체 런너(예: JUnit 4의 매개변수화된 런너) 또는 타사 런너(예: MockitoJUnitRunner)와 함께 Spring TestContext 프레임워크를 사용하려는 경우, 선택적으로Spring의 JUnit 규칙 지원을 대신 사용할 수 있습니다.
다음 코드 목록은 사용자 지정 Spring 런너로 실행되도록 테스트 클래스를 구성하기 위한 최소 요구 사항을 보여줍니다:
@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
@Test
public void testMethod() {
// test logic...
}
}
앞의 예제에서 @TestExecutionListeners는 빈 목록으로 구성되어 기본 리스너를 비활성화하며, 그렇지 않은 경우 @ContextConfiguration을 통해 ApplicationContext를 구성해야 합니다.
Spring JUnit 4 Rules
Org.springframework.test.context.junit4.rules 패키지는 다음과 같은 JUnit 4 규칙을 제공합니다(JUnit 4.12 이상에서 지원됨):
- SpringClassRule
- SpringMethodRule
SpringClassRule은 스프링 테스트 컨텍스트 프레임워크의 클래스 수준 기능을 지원하는 JUnit 테스트 규칙 이고, SpringMethodRule은 스프링 테스트 컨텍스트 프레임워크의 인스턴스 수준 및 메서드 수준 기능을 지원하는 JUnit 메서드 규칙 입니다.
SpringRunner와 달리 Spring의 규칙 기반 JUnit 지원은 org.junit.runner.Runner 구현과 독립적이기 때문에 기존의 대체 런너(예: JUnit 4의 Parameterized) 또는 타사 런너(예: MockitoJUnitRunner)와 결합할 수 있다는 이점이 있습니다.
테스트컨텍스트 프레임워크의 모든 기능을 지원하려면SpringClassRule을 SpringMethodRule과 결합해야 합니다. 다음 예제는 통합 테스트에서 이러한 규칙을 선언하는 적절한 방법을 보여줍니다:
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
public class IntegrationTest {
@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Test
public void testMethod() {
// test logic...
}
}
JUnit 4 Support Classes
Org.springframework.test.context.junit4 패키지는 JUnit 4 기반 테스트 케이스에 대해 다음과 같은 지원 클래스를 제공합니다(JUnit 4.12 이상에서 지원):
- AbstractJUnit4SpringContextTests
- 추상 트랜잭션JUnit4SpringContextTests
AbstractJUnit4SpringContextTests는 스프링 테스트 컨텍스트 프레임워크와 JUnit 4 환경의 명시적 애플리케이션 컨텍스트 테스트 지원을 통합하는 추상 베이스 테스트 클래스입니다. AbstractJUnit4SpringContextTests를 확장하면 명시적 빈 조회를 수행하거나 컨텍스트의 전체 상태를 테스트하는 데 사용할 수 있는보호된 ApplicationContext 인스턴스 변수에 액세스할 수 있습니다.
AbstractTransactionalJUnit4SpringContextTests는 추상 트랜잭션 확장으로, JDBC 액세스를 위한 몇 가지 편의 기능을 추가하는AbstractJUnit4SpringContextTests의 추상 트랜잭션 확장입니다. 이 클래스는 ApplicationContext에 javax.sql.DataSource 빈과PlatformTransactionManager 빈이 정의될 것으로 예상합니다. AbstractTransactionalJUnit4SpringContextTests를 확장하면 SQL 문을 실행하여 데이터베이스를 쿼리하는 데 사용할 수 있는 보호된jdbcTemplate 인스턴스 변수에 액세스할 수 있습니다. 이러한 쿼리를 사용하여 데이터베이스 관련 애플리케이션 코드를 실행하기 전과 후에 데이터베이스 상태를 확인할 수 있으며, Spring은 이러한 쿼리가 애플리케이션 코드와 동일한 트랜잭션 범위에서 실행되도록 보장합니다. ORM 도구와 함께 사용하는 경우 오탐을 피해야 합니다. JDBC 테스트 지원에서 언급했듯이,AbstractTransactionalJUnit4SpringContextTests는 앞서 언급한 jdbcTemplate을 사용하여 JdbcTestUtils의 메서드로 위임하는 편리한 메서드도 제공합니다. 또한 AbstractTransactionalJUnit4SpringContextTests는 구성된 DataSource를 대상으로 SQL 스크립트를 실행하기 위한executeSqlScript(..) 메서드를 제공합니다.
이러한 클래스는 확장을 위한 편의성입니다. 테스트 클래스를 Spring 특정 클래스 계층 구조에 묶지 않으려면 @RunWith(SpringRunner.class) 또는 Spring의 JUnit 규칙을 사용하여 사용자 정의 테스트 클래스를 구성할 수 있습니다 |
SpringExtension for JUnit Jupiter
스프링 테스트컨텍스트 프레임워크는 JUnit 5에 도입된 JUnit 주피터 테스트 프레임워크와의 완전한 통합을 제공합니다. 테스트 클래스에@ExtendWith(SpringExtension.class)로 주석을 달면 표준 JUnit Jupiter 기반 단위 및 통합 테스트를 구현하는 동시에 애플리케이션 컨텍스트 로딩 지원, 테스트 인스턴스의 종속성 주입, 트랜잭션 테스트 메서드 실행 등과 같은 TestContext 프레임워크의 이점을 누릴 수 있습니다.
또한, JUnit Jupiter의 풍부한 확장 API 덕분에 Spring은 JUnit 4 및 TestNG에 대해 Spring이 지원하는 기능 세트 이상의 다음과 같은 기능을 제공합니다:
- 테스트 생성자, 테스트 메서드 및 테스트 라이프사이클 콜백 메서드에 대한 종속성 주입. 자세한 내용은 SpringExtension을 사용한 종속성 주입을 참조하세요.
- SpEL 표현식, 환경 변수, 시스템 속성 등에 기반한 조건부 테스트 실행을 강력하게 지원합니다. 자세한 내용과 예제는Spring JUnit Jupiter 테스트 어노테이션의 @EnabledIf 및 @DisabledIf 설명서를 참조하세요.
- Spring과 JUnit Jupiter의 어노테이션을 결합하는 사용자 정의 구성 어노테이션. 자세한 내용은테스트를 위한 메타 어노테이션 지원에서 @TransactionalDevTestConfig 및 @TransactionalIntegrationTest 예제를 참조하세요.
다음 코드 목록은 @ContextConfiguration과 함께SpringExtension을 사용하도록 테스트 클래스를 구성하는 방법을 보여줍니다:
// Instructs JUnit Jupiter to extend the test with Spring support.
@ExtendWith(SpringExtension.class)
// Instructs Spring to load an ApplicationContext from TestConfig.class
@ContextConfiguration(classes = TestConfig.class)
class SimpleTests {
@Test
void testMethod() {
// test logic...
}
}
JUnit 5에서 어노테이션을 메타 어노테이션으로 사용할 수도 있기 때문에 Spring은 테스트 ApplicationContext 및 JUnit Jupiter의 구성을 단순화하기 위해@SpringJUnitConfig 및 @SpringJUnitWebConfig로 구성된 어노테이션을 제공합니다.
다음 예제에서는 이전 예제에서 사용된 구성의 양을 줄이기 위해 @SpringJUnitConfig를 사용합니다:
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load an ApplicationContext from TestConfig.class
@SpringJUnitConfig(TestConfig.class)
class SimpleTests {
@Test
void testMethod() {
// test logic...
}
}
마찬가지로, 다음 예제에서는 @SpringJUnitWebConfig를 사용하여 JUnit Jupiter와 함께 사용할WebApplicationContext를 생성합니다:
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load a WebApplicationContext from TestWebConfig.class
@SpringJUnitWebConfig(TestWebConfig.class)
class SimpleWebTests {
@Test
void testMethod() {
// test logic...
}
}
자세한 내용은Spring JUnit Jupiter 테스트 어노테이션의 @SpringJUnitConfig 및 @SpringJUnitWebConfig 문서를 참조하세요.
SpringExtension을 사용한 의존성 주입
SpringExtension은 Spring이 테스트 생성자, 테스트 메서드 및 테스트 라이프사이클 콜백 메서드에 대한 종속성 주입을 제공할 수 있게 해주는 JUnit Jupiter의ParameterResolver확장 API를 구현합니다.
구체적으로 SpringExtension은 테스트의ApplicationContext에서 Spring의 @BeforeTransaction 및 @AfterTransaction 또는 JUnit의 @BeforeAll,@AfterAll, @BeforeEach, @AfterEach, @Test, @RepeatedTest, @ParameterizedTest 등으로 주석이 달린 테스트 생성자 및 메서드로 의존성을 주입할 수 있습니다.
생성자 주입
JUnit Jupiter 테스트 클래스의 생성자에 있는 특정 매개 변수가ApplicationContext (또는 그 하위 유형) 유형이거나@Autowired, @Qualifier 또는 @Value로 주석이 지정되거나 메타 주석이 지정된 경우 Spring은 해당 빈 또는 테스트의 ApplicationContext에서 값을 사용하여 해당 특정 매개 변수에 대한 값을 삽입합니다.
생성자가 자동 와이어링 가능한 것으로 간주되는 경우 테스트 클래스 생성자에 대한 모든 인수를 자동 와이어링하도록 Spring을 구성할 수도 있습니다. 생성자는 다음 조건 중 하나가 충족되면 자동 와이어링 가능한 것으로 간주됩니다(우선순위에 따라).
- 생성자에 @Autowired 주석이 있습니다.
- testConstructor가 테스트 클래스에 존재하거나 메타 존재하며 autowireMode속성이 ALL로 설정되어 있습니다.
- 기본 테스트 생성자 자동 와이어 모드가 ALL로 변경되었습니다.
테스트 생성자 사용 및 전역 테스트 생성자 자동 와이어 모드를 변경하는 방법에 대한 자세한 내용은@TestConstructor를 참조하세요.
테스트 클래스의 생성자가 자동 와이어링 가능한 것으로 간주되면 Spring은 생성자의 모든 매개 변수에 대한 인수를 확인할 책임을 맡습니다. 따라서 JUnit Jupiter에 등록된 다른 어떤 ParameterResolver도 이러한 생성자의 매개 변수를 확인할 수 없습니다 |
테스트 메서드 전후에 테스트의 ApplicationContext를 닫는 데 @DirtiesContext가 사용되는 경우 테스트 클래스에 대한 생성자 주입을 JUnit Jupiter의 @TestInstance(PER_CLASS) 지원과 함께 사용해서는 안 됩니다.
그 이유는 @TestInstance(PER_CLASS )가 테스트 메서드 호출 사이에 테스트 인스턴스를 캐시하도록 JUnit Jupiter에 지시하기 때문입니다. 결과적으로 테스트 인스턴스는 이후에 닫힌 ApplicationContext에서 원래 주입된 빈에 대한 참조를 유지합니다. 이러한 시나리오에서는 테스트 클래스의 생성자가 한 번만 호출되므로 종속성 주입이 다시 발생하지 않으며, 후속 테스트는 닫힌 ApplicationContext의 빈과 상호 작용하여 오류가 발생할 수 있습니다.
테스트 메서드 전 또는 테스트 메서드 후 모드와 함께 @TestInstance(PER_CLASS)를 사용하려면 필드 또는 세터 주입을 통해 제공될 Spring의 의존성을 구성하여 테스트 메서드 호출 사이에 다시 주입할 수 있도록 해야 합니다.
|
다음 예제에서는 Spring이 TestConfig.class에서 로드한ApplicationContext의 OrderService 빈을OrderServiceIntegrationTests 생성자에 주입합니다.
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
private final OrderService orderService;
@Autowired
OrderServiceIntegrationTests(OrderService orderService) {
this.orderService = orderService;
}
// tests that use the injected OrderService
}
이 기능을 사용하면 테스트 종속성이 최종적이므로 변경할 수 없습니다.
Spring.test.constructor.autowire.mode 속성이 모두 (@TestConstructor 참조)인 경우, 이전 예제의 생성자에서@Autowired 선언을 생략할 수 있으므로 다음과 같은 결과가 생성됩니다.
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
private final OrderService orderService;
OrderServiceIntegrationTests(OrderService orderService) {
this.orderService = orderService;
}
// tests that use the injected OrderService
}
메서드 주입
JUnit Jupiter 테스트 메서드 또는 테스트 라이프사이클 콜백 메서드의 매개변수가 ApplicationContext (또는 그 하위 유형) 유형이거나@Autowired, @Qualifier 또는 @Value로 주석이 지정되거나 메타 주석이 지정된 경우 Spring은 테스트의 ApplicationContext에서 해당 빈을 사용하여 해당 특정 매개변수의 값을 주입합니다.
다음 예제에서는 Spring이 TestConfig.class에서 로드한 ApplicationContext의 OrderService를 deleteOrder() 테스트 메서드에 주입합니다:
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
@Test
void deleteOrder(@Autowired OrderService orderService) {
// use orderService from the test's ApplicationContext
}
}
JUnit Jupiter의 강력한 ParameterResolver 지원으로 인해 Spring뿐만 아니라 JUnit Jupiter 자체 또는 기타 타사 확장 프로그램에서 여러 종속성을 단일 메서드에 주입할 수도 있습니다.
다음 예제는 Spring과 JUnit Jupiter가 동시에 placeOrderRepeatedly() 테스트 메서드에 종속성을 주입하도록 하는 방법을 보여줍니다.
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
@RepeatedTest(10)
void placeOrderRepeatedly(RepetitionInfo repetitionInfo,
@Autowired OrderService orderService) {
// use orderService from the test's ApplicationContext
// and repetitionInfo from JUnit Jupiter
}
}
JUnit Jupiter에서 @RepeatedTest를 사용하면 테스트 메서드가 RepetitionInfo에 액세스할 수 있다는 점에 유의하세요.
중첩된 테스트 클래스 구성
스프링 테스트 컨텍스트 프레임워크는 스프링 프레임워크 5.0부터 JUnit Jupiter의@Nested 테스트 클래스에서 테스트 관련 어노테이션 사용을 지원했지만, 스프링 프레임워크 5.3까지는 클래스 수준 테스트 구성 어노테이션이 슈퍼클래스에서처럼 둘러싸는 클래스에서 상속되지 않았습니다.
Spring Framework 5.3에서는 테스트 클래스 구성을 둘러싸는 클래스에서 상속하는 것을 일차적으로 지원하며, 이러한 구성은 기본적으로 상속됩니다. 기본 상속 모드에서 오버라이드 모드로 변경하려면, 개별 @Nested 테스트 클래스에@NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)을 주석으로 추가하면 됩니다. 명시적인@NestedTestConfiguration 선언은 어노테이션된 테스트 클래스는 물론 그 서브클래스와 중첩된 클래스에도 적용됩니다. 따라서 최상위 테스트 클래스에 @NestedTestConfiguration으로 주석을 달면 중첩된 모든 테스트 클래스에 재귀적으로 적용될 수 있습니다.
개발 팀이 기본값을 OVERRIDE로 변경할 수 있도록 하려면(예: Spring Framework 5.0~5.2와의 호환성), 클래스 경로의 루트에서 JVM 시스템 속성 또는 spring.properties 파일을 통해 기본 모드를 전역적으로 변경할 수 있습니다. 자세한 내용은 "기본 둘러싸는 구성 상속 모드 변경하기"참고 사항을 참조하세요.
다음의 "Hello World" 예제는 매우 간단하지만, @Nested 테스트 클래스에서 상속되는 최상위 클래스에서 공통 구성을 선언하는 방법을 보여줍니다. 이 특정 예에서는 TestConfig 구성 클래스만 상속됩니다. 중첩된 각 테스트 클래스는 자체 활성 프로파일 세트를 제공하므로 중첩된 각 테스트 클래스에 대해 고유한 ApplicationContext가 생성됩니다(자세한 내용은컨텍스트 캐싱 참조). 중첩된 테스트 클래스에서 상속할 수 있는어노테이션을 확인하려면 지원되는 어노테이션 목록을 참조하세요.
@SpringJUnitConfig(TestConfig.class)
class GreetingServiceTests {
@Nested
@ActiveProfiles("lang_en")
class EnglishGreetings {
@Test
void hello(@Autowired GreetingService service) {
assertThat(service.greetWorld()).isEqualTo("Hello World");
}
}
@Nested
@ActiveProfiles("lang_de")
class GermanGreetings {
@Test
void hello(@Autowired GreetingService service) {
assertThat(service.greetWorld()).isEqualTo("Hallo Welt");
}
}
}
TestNG Support Classes
Org.springframework.test.context.testng 패키지는 TestNG 기반 테스트 케이스에 대해 다음과 같은 지원 클래스를 제공합니다:
- 추상 테스트NG스프링 컨텍스트 테스트
- 추상 트랜잭션 테스트NG스프링 컨텍스트 테스트
추상테스트 기반 테스트 클래스는 Spring TestContext 프레임워크와 TestNG 환경의 명시적 ApplicationContext 테스트 지원을 통합하는 추상 테스트 클래스입니다. AbstractTestNGSpringContextTests를 확장하면 명시적 빈 조회를 수행하거나 컨텍스트의 전체 상태를 테스트하는 데 사용할 수 있는보호된 ApplicationContext 인스턴스 변수에 액세스할 수 있습니다.
추상트랜잭션 테스트는 추상 트랜잭션 확장으로, JDBC 액세스를 위한 몇 가지 편의 기능을 추가하는AbstractTestNGSpringContextTests의 추상 트랜잭션 확장입니다. 이 클래스는 javax.sql.DataSource 빈과PlatformTransactionManager 빈이 ApplicationContext에 정의될 것으로 예상합니다. AbstractTransactionalTestNGSpringContextTests를 확장하면 SQL 문을 실행하여 데이터베이스를 쿼리하는 데 사용할 수 있는 보호된jdbcTemplate 인스턴스 변수에 액세스할 수 있습니다. 이러한 쿼리를 사용하여 데이터베이스 관련 애플리케이션 코드를 실행하기 전과 후에 데이터베이스 상태를 확인할 수 있으며, Spring은 이러한 쿼리가 애플리케이션 코드와 동일한 트랜잭션 범위에서 실행되도록 보장합니다. ORM 도구와 함께 사용하는 경우 오탐을 피해야 합니다. JDBC 테스트 지원에서 언급했듯이,AbstractTransactionalTestNGSpringContextTests는 앞서 언급한 jdbcTemplate을 사용하여 JdbcTestUtils의 메서드로 위임하는 편리한 메서드도 제공합니다. 또한 AbstractTransactionalTestNGSpringContextTests는 구성된 데이터 소스에 대해 SQL 스크립트를 실행하기 위한executeSqlScript(...) 메서드를 제공합니다.
이 클래스는 확장을 위한 편의성입니다. 테스트 클래스를 Spring 특정 클래스 계층 구조에 묶지 않으려면 @ContextConfiguration, @TestExecutionListeners 등을 사용하여 사용자 정의 테스트 클래스를 구성하고 TestContextManager를 사용하여 수동으로 테스트 클래스를 계측할 수 있습니다. 테스트 클래스를 계측하는 방법의 예는 AbstractTestNGSpringContextTests의 소스 코드를 참조하세요 |
테스트에 대한 사전 시간 지원
이 장에서는 스프링 테스트컨텍스트 프레임워크를 사용한 통합 테스트에 대한 스프링의 AOT(Ahead of Time) 지원에 대해 설명합니다.
테스트 지원은 다음과 같은 기능으로 Spring의 핵심 AOT 지원을 확장합니다.
- 테스트 컨텍스트 프레임워크를 사용하여 애플리케이션 컨텍스트를 로드하는 현재 프로젝트의 모든 통합 테스트에 대한 빌드 시간 감지.
-
- 테스트가 현재 프로젝트에 등록된 JUnit PlatformTestEngine을 사용하여 실행되는 한, JUnit Jupiter 및 JUnit 4 기반 테스트 클래스에 대한 명시적 지원은 물론 Spring의 핵심 테스트 어노테이션을 사용하는 TestNG 및 기타 테스트 프레임워크에 대한 암시적 지원도 제공합니다.
- 빌드 시간 AOT 처리: 현재 프로젝트의 각 고유한 테스트 ApplicationContext가 AOT 처리를 위해 새로 고쳐집니다.
- 런타임 AOT 지원: AOT 런타임 모드에서 실행할 때 Spring 통합 테스트는컨텍스트 캐시와 함께 투명하게 참여하는 AOT에 최적화된 ApplicationContext를 사용합니다.
모든 테스트는 기본적으로 AOT 모드에서 활성화됩니다. 그러나@DisabledInAotMode로 어노테이션하여 전체 테스트 클래스 또는 개별 테스트 메서드를 AOT 모드에서 선택적으로 비활성화할 수 있습니다. JUnit Jupiter를 사용하는 경우, Jupiter의 @EnabledInNativeImage 및 @DisabledInNativeImage 어노테이션을 통해 GraalVM 기본 이미지에서 테스트를 선택적으로 활성화 또는 비활성화할 수 있습니다. 또한 @DisabledInAotMode는 JUnit Jupiter의@DisabledInNativeImage 어노테이션과 유사하게 GraalVM 네이티브 이미지 내에서 실행할 때 어노테이션된 테스트 클래스 또는 테스트 메서드를 비활성화합니다.
기본적으로 빌드 시간 AOT 처리 중에 오류가 발생하면 예외가 발생하고 전체 프로세스가 즉시 실패합니다.
오류가 발생한 후에도 빌드 시간 AOT 처리가 계속되도록 하려면 오류를 경고수준 또는 디버그 수준에서 더 자세히 기록하도록 하는 failOnError 모드를 비활성화할 수 있습니다.
명령줄 또는 빌드 스크립트에서 spring.test.aot.processing.failOnError라는 JVM 시스템 속성을 false로 설정하여 failOnError 모드를 비활성화할 수 있습니다. 또는SpringProperties 메커니즘을 통해 동일한 속성을 설정할 수 있습니다.
|
ContextHierarchy 어노테이션은 AOT 모드에서 지원되지 않습니다.
|
GraalVM 네이티브 이미지 내에서 사용할 테스트별 런타임 힌트를 제공하려면 다음과 같은 옵션이 있습니다.
- 사용자 지정TestRuntimeHintsRegistrar를구현하고 META-INF/spring/aot.factories를 통해 전역적으로 등록합니다.
- 커스텀 런타임 힌트 등록기를 구현하고 META-INF/spring/aot.factories를 통해 전역으로 등록하거나 @ImportRuntimeHints를 통해 테스트 클래스에 로컬로 등록합니다.
- Reflective 또는@RegisterReflectionForBinding으로 테스트 클래스에 주석을 달세요.
- Spring의 핵심 런타임 힌트 및 어노테이션 지원에 대한 자세한 내용은 런타임 힌트를 참조하세요.
TestRuntimeHintsRegistrar API는 핵심RuntimeHintsRegistrar API의 동반자 역할을 합니다. 특정 테스트 클래스에 국한되지 않는 테스트 지원을 위한 전역 힌트를 등록해야 하는 경우 테스트별 API보다RuntimeHintsRegistrar를 구현하는 것을 권장합니다.
|
사용자 지정 컨텍스트로더를 구현하는 경우, AOT 빌드 시간 처리 및 AOT 런타임 실행 지원을 제공하려면AotContextLoader를 구현해야 합니다. 그러나 Spring 프레임워크와 Spring Boot에서 제공하는 모든 컨텍스트 로더 구현은 이미 AotContextLoader를 구현하고 있다는 점에 유의하세요.
사용자 정의 TestExecutionListener를 구현하는 경우, AOT 처리에 참여하려면AotTestExecutionListener를구현해야 합니다. 예제는 스프링 테스트 모듈의 SqlScriptsTestExecutionListener를 참조하세요.
'언어 > Spring Docs 번역' 카테고리의 다른 글
Data Access (0) | 2024.07.07 |
---|---|
Testing (2) (0) | 2024.07.07 |
Core Technologies / Null-safety, Data Buffers and Codecs, Logging, Ahead of Time Optimizations (0) | 2024.07.07 |
Core Technologies / Spring AOP APIs (0) | 2024.07.07 |
Core Technologies / Aspect Oriented Programming with Spring (0) | 2024.07.07 |