Cada uno TestContext
proporciona gestión de contexto y soporte de almacenamiento en caché para la
instancia de prueba de la que es responsable. Las instancias de prueba no acceden automáticamente al ApplicationContext
configurado. Sin embargo, si la clase de prueba implementa la interfaz ApplicationContextAware
,
entonces se pasa una referencia a ApplicationContext
a la instancia de prueba. Tenga en cuenta que
AbstractJUnit4SpringContextTests
y AbstractTestNGSpringContextTests
implementan ApplicationContextAware
y, por lo tanto, proporcionan acceso a ApplicationContext
automáticamente.
Como alternativa a implementar la interfaz ApplicationContextAware
Puede inyectar un contexto de
aplicación para su clase de prueba a través de la anotación @Autowired
en un campo o definidor,
como se muestra en el siguiente ejemplo:
@SpringJUnitConfig
class MyTest {
@Autowired
ApplicationContext applicationContext;
// cuerpo de la clase...
}
- Inyectar
ApplicationContext
.
@SpringJUnitConfig
class MyTest {
@Autowired
lateinit var applicationContext: ApplicationContext
// cuerpo de la clase...
}
- Inyectar
ApplicationContext
.
Del mismo modo, si su prueba está configurada para cargar WebApplicationContext
, puede inyectar el
contexto de la aplicación web en la prueba de esta manera:
@SpringJUnitWebConfig
class MyWebAppTest {
@Autowired
WebApplicationContext wac;
// cuerpo de la clase...
}
- Configurar
WebApplicationContext
. - Inyectar
WebApplicationContext
.
@SpringJUnitWebConfig
class MyWebAppTest {
@Autowired
lateinit var wac: WebApplicationContext
// cuerpo de la clase...
}
- Configurar
WebApplicationContext
. - Implemente
WebApplicationContext
.
La inyección de dependencia utilizando la anotación @Autowired
la proporciona el oyente DependencyInjectionTestExecutionListener
,
que está configurado de forma predeterminada (consulte "Inyección de dependencia
para
bancos de pruebas").
Las clases de prueba que utilizan el marco TestContext no necesitan
extender ninguna clase específica ni implementar una interfaz específica para configurar el contexto de su
aplicación. En cambio, la configuración se logra declarando la anotación @ContextConfiguration
a nivel
de clase. Si la clase de prueba no declara explícitamente ubicaciones de recursos de contexto de aplicación o clases
de componentes, el cargador ContextLoader
configurado determina cómo cargar el contexto desde la
ubicación predeterminada o las clases de configuración predeterminadas. Además de las ubicaciones de recursos de
contexto y las clases de componentes, el contexto de la aplicación también se puede configurar usando
inicializadores de contexto de la aplicación.
Las siguientes secciones explican cómo usar la anotación @ContextConfiguration
en Spring para configurar Pruebe ApplicationContext
usando archivos de configuración XML, scripts
Groovy, clases de componentes (generalmente clases con la anotación @Configuration
) o inicializadores
de contexto. También puede implementar y configurar su propio SmartContextLoader
para casos de uso
avanzados.
Configuración de contexto utilizando recursos XML
Para cargar el código ApplicationContext
para sus pruebas utilizando archivos de configuración XML, anote su clase de prueba con
@ContextConfiguration
y configure el atributo locations
con una matriz que contenga datos de ubicación de recursos de
metadatos de configuración XML. Una ruta normal o relativa (por ejemplo, context.xml
) se trata como un
recurso de ruta de clase que hace referencia al paquete en el que se define la clase de prueba. Una ruta que
comienza con una barra diagonal se considera una ubicación de ruta de clase absoluta (por ejemplo, /org/example/config.xml
).
La ruta que representa la URL del recurso (es decir, la ruta con el prefijo classpath:
,
file:
, http:
, etc.) se utiliza tal cual.
@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde "/app-config.xml" y
// "/test-config.xml" en la raíz de la ruta de classpath
@ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"})
class MyTest {
// cuerpo de la clase...
}}
- Establezca el atributo de ubicación en la lista de archivos XML.
@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde " /app-config.xml" y
// "/test-config.xml" en la raíz de la ruta de classpath
@ContextConfiguration("/app-config.xml", "/test-config.xml")
class MyTest {
// cuerpo de la clase...
}
- Establezca el atributo de ubicaciones en una lista de archivos XML.
La anotación @ContextConfiguration
admite un alias para el atributo locations
a
través del atributo estándar value
de Java. Por lo tanto, si no necesita declarar atributos adicionales
en la anotación @ContextConfiguration
, puede omitir la declaración del nombre del atributo locations
y declarar ubicaciones de recursos utilizando el formato abreviado que se muestra en la siguiente ejemplo:
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"})
class MyTest {
// cuerpo de clase...
}
- Especifique archivos XML sin utilizar el atributo
location
.
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-config.xml")
class MyTest {
// cuerpo de clase...
}
- Especificar archivos XML sin usar el
location
de atributo.
Si omites los atributos
location
y
value
en el
@ContextConfiguration
annotation, el marco TestContext intentará determinar la ubicación
predeterminada del recurso XML. Específicamente, GenericXmlContextLoader
y GenericXmlWebContextLoader
definen una ubicación predeterminada basada en el nombre de la clase de prueba. Si su clase se llama com.example.MyTest
,
GenericXmlContextLoader
carga el contexto de la aplicación desde "classpath:com/example/MyTest-context.xml"
.
El siguiente ejemplo muestra cómo hacer esto:
@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration
class MyTest {
// cuerpo de clase...
}}
- Cargue la configuración desde la ubicación predeterminada.
@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration
class MyTest {
// cuerpo de clase...
}
- Cargar el configuración desde la ubicación predeterminada.
Configuración de contexto usando scripts Groovy
Para cargar ApplicationContext
para sus pruebas usando scripts Groovy usando Definición DSL de Groovy
Bean.
También puede anotar su clase de prueba con @ContextConfiguration
y configure el
atributo locations
o value
con una matriz que contenga las ubicaciones de recursos del
script Groovy. La semántica para encontrar recursos para los scripts Groovy es la misma que para los archivos de
configuración XML.
ApplicationContext
en Spring TestContext Framework
se habilita automáticamente si Groovy está en el classpath.
El siguiente ejemplo muestra cómo configurar los archivos de configuración de Groovy:
@ExtendWith(SpringExtension.class)
// ApplicationContext será cargado desde "/AppConfig.groovy" y
// "/TestConfig.groovy" en la raíz del classpath
@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"})
class MyTest {
// cuerpo de la clase...
}
@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde "/AppConfig.groovy" y
// "/TestConfig.groovy" en la raíz del classpath
@ContextConfiguration("/AppConfig.groovy", "/TestConfig.Groovy")
class MyTest {
// cuerpo de clase...
}
- Especificar la ubicación de los archivos de configuración de Groovy.
Si omite las locations
y atributos de valor
de la anotación @ContextConfiguration
,
el marco TestContext intentará determinar un script Groovy predeterminado. Específicamente, GenericGroovyXmlContextLoader
y GenericGroovyXmlWebContextLoader
definen una ubicación predeterminada basada en el nombre de la clase
de prueba. Si su clase se llama com.example.MyTest
, el cargador de contexto Groovy cargará el contexto
de su aplicación desde "classpath:com/example/MyTestContext.groovy"
. El siguiente ejemplo muestra cómo
utilizar el valor predeterminado:
@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde
// "classpath:com/ example/MyTestContext.groovy"
@ContextConfiguration
class MyTest {
// cuerpo de clase...
}
- Cargue la configuración desde la ubicación predeterminada.
@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration
class MyTest {
// cuerpo de clase...
}
- Carga la configuración desde ubicación predeterminada.
Puedes declarar archivos de configuración XML y scripts Groovy al mismo tiempo usando el atributo
location
o value
en @ContextConfiguration
anotación. Si la ruta de ubicación del recurso
configurado termina en .xml
, se carga utilizando XmlBeanDefinitionReader
. De lo
contrario, se carga usando GroovyBeanDefinitionReader
.
La siguiente lista muestra cómo combinar ambas opciones en una prueba de integración:
@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde
// "/app-config.xml" and "/TestConfig.groovy"
@ContextConfiguration({ "/app-config.xml", "/TestConfig.groovy" })
class MyTest {
// cuerpo de la clase...
}
@ExtendWith (SpringExtension::class)
// ApplicationContext se cargará desde
// "/app-config.xml" and "/TestConfig.groovy"
@ContextConfiguration("/app-config.xml", "/TestConfig.groovy")
class MyTest {
// cuerpo de clase...
}
Configuración de contexto usando clases de componentes
Para cargar ApplicationContext
para sus pruebas usando clases de componentes (ver: sección "Configuración de contenedor
basada en Java"), puede anotar su clase de prueba con @ContextConfiguration
y configure el
atributo classes
con una matriz que contenga referencias a las clases de componentes. El siguiente
ejemplo muestra cómo hacer esto:
@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde AppConfig y TestConfig
@ContextConfiguration(classes = { AppConfig.class, TestConfig.class})
class MyTest {
// cuerpo de clase...
}
- Especificar clases de componentes.
@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde AppConfig y TestConfig
@ContextConfiguration(classes = [AppConfig::class, TestConfig::class])
class MyTest {
// cuerpo de clase...
}
- Especificar componente clases.
El término "clase de componente" puede referirse a cualquiera de los siguientes:
Una clase anotada con
@Configuration
.Un componente (es decir, una clase marcada con las anotaciones
@Component
,@Service
,@Repository
u otras anotaciones genéricas).Una clase compatible con JSR-330 anotada con anotaciones
javax.inject
.Cualquier clase que contenga un método anotado con
@Bean
.Cualquier otra clase destinada para ser registrado como un frijol Spring (es decir. Spring Bean en
ApplicationContext
), aprovechando potencialmente la detección automática y el enlace de un único constructor sin el uso de anotaciones en Spring.
Ver javadoc en @Configuración
y @Bean
para obtener más información sobre la configuración y la
semántica de las clases de Bean, con especial atención a la anotación con @Bean
en modo ligero.
Si omite el atributo classes
en la anotación @ContextConfiguration
, el marco
TestContext intentará determinar el presencia de clases de configuración predeterminadas. Específicamente, los
cargadores AnnotationConfigContextLoader
y AnnotationConfigWebContextLoader
ubican
todas las clases estáticas anidadas de una clase de prueba que cumplen con los requisitos de implementación de
la clase de configuración como se especifica en el javadoc en @Configuration
. Tenga en cuenta que el nombre de la clase de
configuración es arbitrario. Además, una clase de prueba puede contener opcionalmente más de una clase de
configuración anidada estática. En el siguiente ejemplo, la clase OrderServiceTest
declara una
clase de configuración anidada estática denominada Config
que se utiliza automáticamente para
cargar ApplicationContext
para la clase de prueba:
@SpringJUnitConfig
// ApplicationContext se cargará desde
// clase anidada estática Config class
class OrderServiceTest {
@Configuration
static class Config {
// este bean se inyectará en la clase OrderServiceTest
@Bean OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
// establecer propiedades, etc.
return orderService;
}
}
@Autowired
OrderService orderService;
@Test
void testOrderService() {
// prueba orderService
}
}
- Cargando información de configuración desde un archivo anidado clase
Config
.
@SpringJUnitConfig
// ApplicationContext se cargará desde la clase de configuración anidada class
class OrderServiceTest {
@Autowired
lateinit var orderService: OrderService
@Configuration
class Config {
// este bean se inyectará en la clase OrderServiceTest
@Bean
fun orderService(): OrderService {
// establecer propiedades, etc.
return OrderServiceImpl()
}
}
@Test
fun testOrderService() {
// prueba orderService
}
}
- Cargando información de configuración desde una clase
Config
anidada.
Combinación de XML, scripts Groovy y clases de componentes
A veces es deseable mezclar XML archivos de configuración Scripts Groovy y clases de componentes (generalmente
clases marcadas con la anotación @Configuration
) para configurar el ApplicationContext
para sus pruebas. Por ejemplo, si está utilizando la configuración XML en un entorno de producción, puede
decidir que vale la pena recurrir a clases marcadas con la anotación @Configuration
para configurar
beans específicos administrados por Spring para sus pruebas, o viceversa. viceversa.
Además, algunos marcos de terceros (como Spring Boot) brindan un excelente soporte para cargar ApplicationContext
desde diferentes tipos de recursos (como archivos de configuración XML, scripts Groovy, y clases anotadas) al
mismo tiempo @Configuration
). Históricamente, Spring Framework no ha admitido esto para
implementaciones estándar. En consecuencia, la mayoría de las implementaciones de
SmartContextLoader
que Spring Framework proporciona en el módulo spring-test
admiten
solo un tipo de recurso por contexto de prueba. Sin embargo, esto no significa que no se puedan utilizar ambas
opciones. La excepción a la regla general es que GenericGroovyXmlContextLoader
y GenericGroovyXmlWebContextLoader
admiten archivos de configuración XML y scripts Groovy. Además, los marcos de terceros pueden admitir la
declaración de locations
y clases
a través de la anotación
@ContextConfiguration
y con soporte de pruebas estándar en el marco TestContext. tiene Hay opciones que se describen a continuación.
Si necesita usar ubicaciones de recursos (como XML o Groovy) y clases anotadas con @Configuration
para configurar sus pruebas, entonces necesita para elegir algo: uno de estos como punto de entrada, y esa clase
o ubicación seleccionada debe incluir o importar las demás. Por ejemplo, en scripts XML o Groovy, puede incluir
clases anotadas con @Configuration
mediante el escaneo de beans o definiéndolas como Spring beans
normales, mientras que en una clase anotada con @Configuration
, puede utilizar la anotación @ImportResource
para importar archivos de configuración XML o scripts Groovy. Tenga en cuenta que esta lógica operativa es
semánticamente equivalente a cómo configura su aplicación en un entorno de producción: en una configuración de
producción, define un conjunto de ubicaciones de recursos XML o Groovy, o un conjunto de clases marcadas con
@Configuración
anotación, desde la cual se carga su ApplicationContext
de
producción; sin embargo, aún tiene la opción de incluir o importar otro tipo de configuración.
Configuración de contextos usando inicializadores de contexto
Para configurar ApplicationContext
para sus pruebas con inicializadores de contexto, anote su clase
de prueba con @ContextConfiguration
y configure el atributo initializers
con una
matriz que contiene referencias de clases, implementando ApplicationContextInitializer
. Los
inicializadores de contexto declarados se utilizan luego para inicializar el
ConfigurableApplicationContext
que se carga para sus pruebas. Tenga en cuenta que el tipo ConfigurableApplicationContext
particular admitido por cada inicializador declarado debe ser compatible con el tipo
ApplicationContext
que crea el SmartContextLoader
utilizado (normalmente GenericApplicationContext
). Además, el orden en el que se llaman los inicializadores depende de si implementan la interfaz
Ordered
de Spring o si están marcados con la anotación @Order
o el estándar @Priority
anotación. El siguiente ejemplo muestra cómo utilizar inicializadores:
@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde TestConfig
// y se inicializará usando TestAppCtxInitializer
@ContextConfiguration(
classes = TestConfig.class,
initializers = TestAppCtxInitializer.class)
class MyTest {
// cuerpo de clase...
}
- Establecer la configuración utilizando una clase de configuración y un inicializador.
@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde TestConfig
// y se inicializará usando TestAppCtxInitializer
@ContextConfiguration(
classes = [TestConfig::class],
initializers = [TestAppCtxInitializer::class ])
class MyTest {
// cuerpo de clase...
}
- Especificar la configuración usando una clase de configuración y un inicializador.
También es posible omitir completamente la declaración de archivos de configuración XML, Groovy scripts o
clases de componentes en la anotación @ContextConfiguration
y en su lugar declarar solo las clases
ApplicationContextInitializer
, que luego serán responsables de registrar beans con el contexto, por
ejemplo, mediante programación cargar definiciones de beans desde archivos XML o clases de configuración. El
siguiente ejemplo muestra cómo hacer esto:
@ExtendWith(SpringExtension.class)
// El ApplicationContext se inicializará con EntireAppInitializer
// lo cual se supone para registrar beans en el contexto
@ContextConfiguration(initializers = EntireAppInitializer.class)
class MyTest {
// cuerpo de clase...
}
- Establezca la configuración usando un solo inicializador.
@ExtendWith(SpringExtension::class)
// El ApplicationContext se inicializará usando EntireAppInitializer
// que se supone registra beans en el contexto
@ContextConfiguration(initializers = [EntireAppInitializer::class])
class MyTest {
// cuerpo de clase...
}
- Establecer la configuración usando un solo inicializador.
Heredar la configuración de contexto
La @ContextConfiguration annotation
admite atributos booleanos inheritLocations
y inheritInitializers
que indican si las ubicaciones de recursos o las clases de componentes y los inicializadores de contexto declarados
por las superclases deben heredarse. El valor predeterminado para ambas banderas es true
. Esto
significa que la clase de prueba hereda ubicaciones de recursos o clases de componentes, así como inicializadores de
contexto declarados por cualquier superclase. Específicamente, las ubicaciones de recursos o clases de componentes
para la clase de prueba se agregan a la lista de ubicaciones de recursos o clases anotadas declaradas como
superclases. De manera similar, los inicializadores para una clase de prueba determinada se agregan al conjunto de
inicializadores definidos por las superclases de prueba. Por lo tanto, las subclases pueden ampliar ubicaciones de
recursos, clases de componentes o inicializadores de contexto.
Si el atributo inheritLocations
o
inheritInitializers
está en @ContextConfiguratio.n
anotación se establece en
false
, luego las ubicaciones de recursos o las clases de componentes y los inicializadores de contexto
para la clase de prueba respectivamente "sombrean" y anulan efectivamente la configuración definida por las
superclases.
" para obtener más detalles @ Anidado
".
En el siguiente ejemplo, que utiliza ubicaciones de recursos XML, ApplicationContext
para ExtendedTest
se carga desde base-config.xml
y extended-config.xml
en
ese orden. Por lo tanto, los beans definidos en extended-config.xml
pueden anular (es decir,
reemplazar) los beans definidos en base-config.xml
. El siguiente ejemplo muestra cómo una clase puede
extender otra y usar tanto su propio archivo de configuración como el archivo de configuración de la superclase:
@ExtendWith(SpringExtension.class )
// ApplicationContext se cargará desde "/base-config.xml"
// en la ruta de classpath @ContextConfiguration("/base-config.xml")
class BaseTest {
// cuerpo de clase...
}
// ApplicationContext se cargará desde "/base-config.xml" y
// "/extended-config.xml" en la ruta de classpath
@ContextConfiguration("/extended-config.xml")
class ExtendedTest extends BaseTest {
// cuerpo de clase...
}
- Archivo de configuración definido en la superclase.
- Archivo de configuración definido en la subclase.
@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde "/base-config.xml"
// en la ruta de classpath
@ContextConfiguration("/base-config.xml")
open class BaseTest {
// cuerpo de clase..
}
// ApplicationContext se cargará desde "/base-config.xml" and
// "/extended-config.xml" en la ruta de classpath
@ContextConfiguration("/extended-config.xml")
class ExtendedTest : BaseTest () {
// cuerpo de la clase...
}
- Archivo de configuración definido en la superclase.
- El archivo de configuración definido en la subclase.
De manera similar, en el siguiente ejemplo, que utiliza clases de componentes, ApplicationContext
para ExtendedTest
se carga desde las clases BaseConfig
y ExtendedConfig
en
ese orden. Por lo tanto, los beans definidos en ExtendedConfig
pueden anular (es decir, reemplazar) los
beans definidos en BaseConfig
. El siguiente ejemplo muestra cómo una clase puede extender a otra y usar
tanto su propia clase de configuración como la clase de configuración de la superclase:
// ApplicationContext se cargará desde BaseConfig
@SpringJUnitConfig(BaseConfig.class)
class BaseTest {
// cuerpo de clase...
}
// ApplicationContext se cargará desde BaseConfig y ExtendedConfig
@SpringJUnitConfig(ExtendedConfig.class)
class ExtendedTest extends BaseTest {
// cuerpo de clase...
}
- Clase de configuración definida en una superclase.
- Clase de configuración definida en una subclase.
// ApplicationContext se cargará desde BaseConfig
@SpringJUnitConfig(BaseConfig::class)
open class BaseTest {
// cuerpo de clase...
}
// ApplicationContext se cargará desde BaseConfig y ExtendedConfig
@SpringJUnitConfig(ExtendedConfig::class)
class ExtendedTest : BaseTest() {
// cuerpo de la clase...
}
- Configuración clase, definida en la superclase.
- Clase de configuración definida en la subclase.
En el siguiente ejemplo, que utiliza inicializadores de contexto, ApplicationContext
para ExtendedTest
se inicializa usando BaseInitializer
y ExtendedInitializer
. Sin embargo, tenga en cuenta
que el orden en el que se llaman los inicializadores depende de si implementan la interfaz Ordered
de
Spring o si están marcados con la anotación @Order
de Spring o el anotación @Priority
estándar. El siguiente ejemplo muestra cómo una clase puede extender a otra y usar tanto su propio
inicializador como el inicializador de la superclase:
// ApplicationContext será inicializado por BaseInitializer
@SpringJUnitConfig (initializers = BaseInitializer.class)
class BaseTest {
// cuerpo de clase...
}
// ApplicationContext será inicializado por BaseInitializer
// y ExtendedInitializer
@SpringJUnitConfig(initializers = ExtendedInitializer.class)
ExtendedTest extends BaseTest {
// cuerpo de la clase...
}
- Inicializador definido en la superclase.
- Inicializador definido en una subclase.
// ApplicationContext se inicializará BaseInitializer
@SpringJUnitConfig(initializers = [BaseInitializer::class])
open class BaseTest {
// cuerpo de la clase...
}
// ApplicationContext será inicializado por BaseInitializer
// y ExtendedInitialize
@SpringJUnitConfig(initializers = [ExtendedInitializer::class])
class ExtendedTest : BaseTest() {
// cuerpo de la clase...
}
- Inicializador definido en la superclase.
- Inicializador definido en la subclase.
Configuración de contexto utilizando perfiles de entorno
Spring Framework ofrece soporte de primera clase para el concepto de entornos y perfiles (también conocidos como
"perfiles de definición de beans"), y Las pruebas de integración se pueden configurar para activar perfiles
específicos que definen beans para varios escenarios de prueba. Esto se logra anotando la clase de prueba con la
anotación @ActiveProfiles
y proporcionando una lista de perfiles que se activarán cuando se cargue
ApplicationContext
para la prueba.
@ActiveProfiles
con cualquier implementación de la interfaz SPI SmartContextLoader
, pero
el anotación @ActiveProfiles
no es compatible con implementaciones mayores que la antigua interfaz SPI
ContextLoader
.
Veamos dos ejemplos con configuración XML y clases anotadas con @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 se cargará desde "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// prueba transferService
}
}
@ExtendWith(SpringExtension:: class)
// ApplicationContext se cargará desde "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// prueba transferService
}
}
Cuando se ejecuta TransferServiceTest
, su ApplicationContext
se carga desde
el archivo de configuración app-config.xml
en la raíz del classpath. Si observa
app-config.xml
, puede ver que el bean AccountRepository
depende del bean
DataSource
. Sin embargo, dataSource
no está definido como un bean de alto nivel. En
cambio, dataSource
se define tres veces: en el perfil production
, en el perfil
dev
y en el default
. perfil.
Al marcar TransferServiceTest
con la
anotación @ActiveProfiles("dev")
, le indicamos al Spring TestContext Framework que cargue el ApplicationContext
con perfiles activos establecidos en {"dev"}
. Como resultado, se crea una base de datos integrada, que
se completa con datos de prueba, y el bean accountRepository
se adjunta con un enlace al DataSource
de desarrollo. Esto es probablemente lo que desea lograr en una prueba de integración.
A veces tiene sentido
asignar beans al perfil predeterminado
. Los beans en un perfil están habilitados de forma
predeterminada solo si no se ha habilitado específicamente ningún otro perfil. Esto se puede utilizar para definir
los beans de "retorno" que se utilizarán en el estado predeterminado de la aplicación. Por ejemplo, puede
especificar explícitamente la fuente de datos para dev
y production
. perfiles, pero define
la fuente de datos en la memoria como la fuente predeterminada si ninguna está activa.
Los siguientes
listados de código demuestran cómo implementar la misma configuración y prueba de integración usando clases con
anotación de
@Configuration
en lugar de XML:
@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("dev")
class StandaloneDataConfig {
@Bean
fun dataSource(): DataSource {
return 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("production")
class JndiDataConfig {
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
val ctx = InitialContext()
return ctx.lookup("java:comp/env/jdbc/datasource") as 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
@Profile("default")
class DefaultDataConfig {
@Bean
fun dataSource(): DataSource {
return 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();
}
}
@Configuration
class TransferServiceConfig {
@Autowired
lateinit var dataSource: DataSource
@Bean
fun transferService(): TransferService {
return DefaultTransferService(accountRepository(), feePolicy())
}
@Bean
fun accountRepository(): AccountRepository {
return JdbcAccountRepository(dataSource)
}
@Bean
fun feePolicy(): FeePolicy {
return ZeroFeePolicy()
}
}
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// prueba transferService
}
}
@SpringJUnitConfig(
TransferServiceConfig::class,
StandaloneDataConfig::class,
JndiDataConfig::class,
DefaultDataConfig::class)
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// prueba transferService
}
}
En esta variante, dividimos la configuración XML en cuatro clases independientes, marcadas con la
anotación @Configuration
:
TransferServiceConfig
: obtienedataSource
mediante inyección de dependencia utilizando la anotación@Autowired
.StandaloneDataConfig
: DefinedataSource
para una base de datos integrada, adecuada para pruebas de desarrollador.JndiDataConfig
: DefinedataSource
que se recuperó de JNDI en producción.DefaultDataConfig
: define eldataSource
para la base de datos integrada predeterminada si el perfil no está activo.
Como en el ejemplo de configuración basada en XML, aún anotamos TransferServiceTest
con la
anotación @ActiveProfiles("dev")
, pero esta vez especificamos las cuatro clases de configuración
usando la anotación @ContextConfiguration
. El cuerpo de la clase de prueba en sí permanece
completamente sin cambios.
A menudo sucede que un conjunto de perfiles se utiliza en varias clases de prueba
dentro del mismo proyecto. Por lo tanto, para evitar declaraciones duplicadas de la anotación
@ActiveProfiles
, puede declarar la anotación @ActiveProfiles
una vez para la clase
principal, y las subclases heredarán automáticamente la configuración con el @ActiveProfiles
código
de anotación> de la clase principal. En el siguiente ejemplo, la declaración de la anotación
@ActiveProfiles
(así como otras anotaciones) se ha movido a la superclase abstracta AbstractIntegrationTest
:
@Nested
para obtener más detalles"
.
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
@SpringJUnitConfig(
TransferServiceConfig::class,
StandaloneDataConfig::class,
JndiDataConfig::class,
DefaultDataConfig::class)
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
// perfil "dev" hereda de la superclase
class TransferServiceTest extends AbstractIntegrationTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// prueba transferService
}
}
// perfil "dev" hereda de la clase superclase
class TransferServiceTest : AbstractIntegrationTest() {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// prueba transferService
}
}
La anotación @ActiveProfiles
también admite el atributo inheritProfiles
,
que se puede utilizar para deshabilitar la herencia de perfiles activos, como se muestra en el siguiente ejemplo:
// perfil "dev" se reemplaza por "producción"
@ActiveProfiles(profiles = "production", inheritProfiles = false)
class ProductionTransferServiceTest extends AbstractIntegrationTest {
// test body
}
// el perfil "dev" se reemplaza por "producción"
@ActiveProfiles("production", inheritProfiles = false)
class ProductionTransferServiceTest : AbstractIntegrationTest() {
// test body
}
Además, a veces es necesario resolver perfiles activos para pruebas mediante programación, en lugar de mediante declaración. - por ejemplo, basado en:
El sistema operativo actual.
El hecho de que las pruebas se estén ejecutando en el servidor de compilación para asegurar una integración continua
La presencia de ciertas variables de entorno.
La presencia de anotaciones personalizadas a nivel de clase.
Otra funcionalidad transversal.
Para resolver mediante programación perfiles de definición de beans activos, puede implementar un id=personalizado
"testcontext-ctx-management-env-profiles-ActiveProfilesResolver" ActiveProfilesResolver
y regístrelo con el attribut
resolver
en la anotación @ActiveProfiles
. Para obtener más información, consulte
el
javadoc. El siguiente ejemplo demuestra cómo implementar y registrar un OperatingSystemActiveProfilesResolver
personalizado:
// el perfil "dev" se anula mediante programación usando un solucionador personalizado
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver.class,
inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
// el perfil "dev" se anula mediante programación utilizando un solucionador personalizado
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver::class,
inheritProfiles = false)
class TransferServiceTest : AbstractIntegrationTest() {
// test body
}
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
@Override
public String[] resolve(Class<?> testClass) {
String profile = ...;
// determina el valor del perfil según el sistema operativo
return new String[] {profile};
}
}
class OperatingSystemActiveProfilesResolver : ActiveProfilesResolver {
override fun resolve(testClass: Class<*>) : Array<String> {
val profile: String = ...
// determina el valor del perfil según el sistema operativo
return arrayOf(profile)
}
}
Configuración de contexto usando fuentes de propiedades de prueba
Spring Framework ofrece soporte de primera clase para el concepto de entorno con una jerarquía de fuentes de
propiedades, por lo que puede configurar pruebas de integración usando test fuentes de propiedad. A diferencia de la
anotación @PropertySource
utilizada en las clases @Configuration
, la anotación @TestPropertySource
se puede declarar en una clase de prueba para declarar ubicaciones de recursos para archivos de prueba. propiedades
o propiedades incorporadas. Estas fuentes de propiedades de prueba se agregan al PropertySources
establecido en el Environment
para el ApplicationContext
cargado para la prueba de
integración anotada.
Puede utilizar la anotación @TestPropertySource
con
cualquier implementación de la interfaz SPI SmartContextLoader
, pero la anotación @TestPropertySource
no
se admite cuando se utiliza la implementación anterior de SPI ContextLoader
.
Las implementaciones SmartContextLoader
acceden a la fuente de propiedad de prueba agrupada valores
a través de los métodos getPropertySourceLocations()
y getPropertySourceProperties()
en
MergedContextConfiguration
.
Declaración de fuentes de propiedades de prueba
Puede configurar la prueba archivos de propiedades que utilizan el atributo ubicaciones
o
value
en la anotación @TestPropertySource
.
Archivo de propiedades tanto
tradicional como basado en XML Se admiten formatos, por ejemplo,
"classpath:/com/example/test.properties"
o "file:///path/to/file.xml"
.
Cada ruta se
interpreta como Recurso
de Spring. Una ruta simple (por ejemplo, "test.properties"
) se
considera un recurso de classpath que hace referencia al paquete en el que se define la clase de prueba. Una ruta
que comienza con una barra diagonal se trata como un recurso de ruta de clase absoluta (por ejemplo: "/org/example/test.xml"
).
La ruta que hace referencia a la URL (por ejemplo, una ruta con el prefijo classpath:
,
file:
o http:
) se carga utilizando el archivo especificado protocolo de recursos. No se
permiten comodines de ubicación de recursos (como */.properties
): cada ubicación debe representar
exactamente un recurso .properties
o .xml
.
El siguiente ejemplo utiliza un archivo de propiedades de prueba:
@ContextConfiguration
@TestPropertySource("/test.properties")
class MyIntegrationTests {
// cuerpo de clase...
}
- Establezca el archivo de propiedades con una ruta absoluta.
@ContextConfiguration
@TestPropertySource("/test.properties" )
class MyIntegrationTests {
// cuerpo de clase...
}
- Especifique el archivo de propiedades con una ruta absoluta.
Puede configurar propiedades en línea
como pares clave-value usando el atributo properties
en la anotación @TestPropertySource
,
como se muestra en el siguiente ejemplo. Todos los pares clave-valor se agregan al Environment
adjunto como una prueba PropertySource
con el nivel de precedencia más alto.
La sintaxis admitida para pares clave-valor es la misma que la sintaxis definida para las entradas en el archivo de propiedades de Java:
clave=value
clave:value
valor value
En el siguiente ejemplo, las dos propiedades en línea:
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"})
class MyIntegrationTests {
// cuerpo de la clase...
}
- Establezca dos propiedades usando dos sintaxis clave-valor.
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"])
class MyIntegrationTests {
// cuerpo de clase...
}
- Establezca dos propiedades utilizando dos sintaxis clave-valor.
A partir de Spring Framework 5.2, la anotación
@TestPropertySource
se puede utilizar como una anotación repetida. Esto significa que puede tener múltiples declaraciones
de anotaciones @TestPropertySource
en una clase de prueba, con locations
y
properties
de anotaciones posteriores @TestPropertySource
anulará las propiedades de
las anotaciones @TestPropertySource
anteriores.
También es posible declarar múltiples
anotaciones compuestas para una clase de prueba, cada una metaanotada con @TestPropertySource
, y
todas estas declaraciones de anotaciones @TestPropertySource
contribuirán a las fuentes de sus
propiedades de prueba.
Las anotaciones @TestPropertySource
expuestas directamente siempre se
toman prioridad sobre las anotaciones @TestPropertySource
metapresentadas. En otras palabras, las
locations
y las properties
de una anotación @TestPropertySource
directamente presente anularán las locations
y las propiedades.
de la anotación
@TestPropertySource
utilizada como metaanotación.
Detección de archivos de propiedades predeterminada
Si la anotación @TestPropertySource
se declara como
una anotación vacía (es decir, sin valores explícitos para los atributos locations
o
properties
),
se intenta descubrir el archivo de propiedades predeterminado relativo a la clase que declaró la anotación. Por
ejemplo, si la clase de prueba anotada es com.example.MyTest
, entonces el archivo de propiedades
predeterminado correspondiente es classpath:com/example/MyTest.properties
. Si no se puede determinar el
valor predeterminado, se generará una excepción IllegalStateException
.
Precedencia
Las propiedades de prueba tienen un mayor
nivel de precedencia que las propiedades definidas en el entorno del sistema operativo, las propiedades del sistema
Java o las fuentes de propiedades agregadas de forma declarativa por la aplicación mediante
@PropertySource
anotación o mediante programación. Por lo tanto, las propiedades de prueba se pueden
utilizar para anular
selectivamente las propiedades cargadas desde fuentes de propiedades del sistema y de la aplicación. Además, las
propiedades en línea tienen un mayor nivel de prioridad que las propiedades cargadas desde ubicaciones de recursos.
Sin embargo, tenga en cuenta que las propiedades registradas mediante la anotación
@DynamicPropertySource
tienen un nivel de precedencia más alto que las propiedades cargadas mediante la
anotación @TestPropertySource
.
A continuación Por ejemplo, las propiedades
timezone
y port
, así como cualquier propiedad definida en el archivo "/test.properties"
,
anulan cualquier propiedad del mismo nombre definido en las fuentes de propiedades del sistema y de la aplicación.
Además, si el archivo "/test.properties"
define entradas para las propiedades timezone
y
port
, el archivo integrado las anula. Propiedades declaradas con el atributo properties
.
El siguiente ejemplo muestra cómo establecer propiedades tanto en un archivo como en línea:
@ContextConfiguration
@TestPropertySource(
locations = "/test.properties",
properties = {"timezone = GMT", "port: 4242"}
)
class MyIntegrationTests {
// cuerpo de la clase...
}
@ContextConfiguration
@TestPropertySource("/test.properties",
properties = ["timezone = GMT", "port: 4242"]
)
class MyIntegrationTests {
// cuerpo de clase...
}
Heredar y anular fuentes de propiedades de prueba
La anotación @TestPropertySource
admite los atributos
booleanos inheritLocations
y heredarProperties
que indican si las ubicaciones de
recursos deben heredarse para archivos de propiedades y propiedades en línea declaradas por superclases. El
valor predeterminado para ambas banderas es true
. Esto significa que la clase de prueba hereda
ubicaciones y propiedades en línea declaradas por cualquier superclase. Específicamente, las ubicaciones y
propiedades en línea de la clase de prueba se agregan a las ubicaciones y propiedades en línea declaradas por
las superclases. De esta manera, las subclases pueden ampliar ubicaciones y propiedades en línea. Tenga en
cuenta que las propiedades que aparecen más tarde "eclipsan" (es decir, anulan) las propiedades del mismo nombre
que aparecen antes. Además, las reglas de precedencia anteriores también se aplican a las fuentes de propiedades
de prueba heredadas.
Si el atributo inheritLocations
o inheritPropertie.s
está
en anotación @TestPropertySource
se establece en false
, las ubicaciones o propiedades en
línea para la clase de prueba en consecuencia "sombrean" y anulan efectivamente la configuración definida por
las superclases.
"@Nested
para obtener más detalles"
.
En el siguiente ejemplo, el ApplicationContext
para BaseTest
se carga únicamente
utilizando el archivo base.properties
como fuente de las propiedades de la prueba. Al mismo tiempo, el
ApplicationContext
para ExtendedTest
se carga utilizando los archivos base.properties
y extended.properties
. Como propiedades de fuentes de prueba. El siguiente ejemplo muestra cómo definir
propiedades tanto en una subclase como en su superclase usando archivos properties
:
@TestPropertySource ("base.properties")
@ContextConfiguration
class BaseTest {
// ...
}
@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
// ...
}
@TestPropertySource("base.properties")
@ContextConfiguration
open class BaseTest {
// ...
}
@TestPropertySource("extended.properties" )
@ContextConfiguration
class ExtendedTest : BaseTest() {
// ...
}
En el siguiente ejemplo ApplicationContext
para BaseTest
se carga usando solo la
propiedad en línea key1
. Al mismo tiempo, el ApplicationContext
para
ExtendedTest
se carga utilizando las propiedades en línea key1
y key2
. El
siguiente ejemplo muestra cómo definir propiedades tanto en una subclase como en su superclase usando propiedades en
línea:
@TestPropertySource(properties = "key1 = value1 ")
@ContextConfiguration
class BaseTest {
// ...
}
@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
// ...
}
@TestPropertySource(properties = ["key1 = value1"])
@ContextConfiguration
open class BaseTest {
// ...
}
@TestPropertySource(properties = [" key2 = value2"])
@ContextConfiguration
class ExtendedTest : BaseTest() {
// ...
}
Configuración de contexto usando fuentes de propiedades dinámicas
A partir de Spring Framework 5.2.5, el marco TestContext proporciona soporte para
propiedades dinámicas usando @DynamicPropertySource
código de anotación. Esta anotación se
puede utilizar en pruebas de integración que necesitan agregar propiedades con valores dinámicos al PropertySources
establecido en el Environment
para el ApplicationContext
cargado. Para la prueba de
integración.
La anotación @DynamicPropertySource
y su infraestructura de
soporte se diseñaron originalmente para permitir propiedades de Las pruebas basadas en Testcontainers podrían exponerse fácilmente a las
pruebas de integración de Spring. Sin embargo, esta función también se puede utilizar con cualquier forma de recurso
externo cuyo ciclo de vida se mantenga fuera de la prueba ApplicationContext
.
A diferencia
de @TestPropertySource
anotación de que se aplica a nivel de clase,
@DynamicPropertySource
debe aplicarse a un método estático que toma un único argumento DynamicPropertyRegistry
,
que se usa para agregar nombre-pares de valores en Environment
. Los valores son dinámicos
y se especifican a través de Proveedor
, al que solo se llama cuando se resuelve la propiedad.
Normalmente, las referencias a métodos se utilizan para especificar valores, como se ve en el siguiente ejemplo,
que utiliza el proyecto Testcontainers para administrar un contenedor Redis fuera de
ApplicationContext
de Spring. La dirección IP y el puerto del contenedor Redis administrado están disponibles para los componentes en
ApplicationContext
de la prueba a través de redis.host
y redis.port
.
propiedades. Se puede acceder a estas propiedades a través de la abstracción Environment
de Spring
o implementarlas directamente en beans administrados por Spring, por ejemplo, a través de @Value("${redis.host}".)
anotaciones y @Value("${redis.port}")
, respectivamente.
Si las anotaciones @DynamicPropertySource
se usa en la clase
principal y descubre que las pruebas en las subclases han fallado porque las propiedades dinámicas cambian entre
subclases, es posible que necesite anotar la clase principal con @DirtiesContext
para que se garantice
que cada subclase reciba su propio ApplicationContext
con las propiedades dinámicas correctas.
@SpringJUnitConfig(/* . .. */)
@Testcontainers
class ExampleIntegrationTests {
@Container
static RedisContainer redis = new RedisContainer();
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("redis.host", redis::getContainerIpAddress);
registry.add("redis.port", redis::getMappedPort);
}
// tests...
}
@SpringJUnitConfig(/* ... */)
@Testcontainers
class ExampleIntegrationTests {
companion object {
@Container
@JvmStatic
val redis: RedisContainer = RedisContainer()
@DynamicPropertySource
@JvmStatic
fun redisProperties(registry: DynamicPropertyRegistry) {
registry.add("redis.host", redis::getContainerIpAddress)
registry.add("redis.port", redis::getMappedPort)
}
}
// pruebas...
}
Precedencia
Propiedades dinámicas tienen un nivel de precedencia más alto que las propiedades
cargadas desde la anotación @TestPropertySource
, el entorno del sistema operativo, las propiedades del
sistema Java o las fuentes de propiedades agregadas de forma declarativa por la aplicación utilizando la anotación
@PropertySource
o programáticamente. Por lo tanto, las propiedades dinámicas se pueden utilizar para
anular selectivamente las propiedades cargadas a través de la anotación @TestPropertySource
, las
fuentes de propiedades del sistema y las fuentes de propiedades de la aplicación.
Cargando WebApplicationContext
Para indicarle al marco TestContext que cargue WebApplicationContext
en lugar del ApplicationContext
estándar, puede anotar la clase de prueba correspondiente con @WebAppConfiguration
.
La
presencia de la anotación @WebAppConfiguration
en su clase de prueba le indica al TestContext Framework
(TCF) que el WebApplicationContext
(WAC) debe cargarse para sus pruebas de integración. En segundo
plano, TCF garantiza que se cree un MockServletContext
y se pase al WAC de su prueba. De forma
predeterminada, la ruta de recursos base para su MockServletContext
está configurada en src/main/webapp
.
Se interpreta como una ruta relativa a la raíz de su JVM (generalmente la ruta a su proyecto). Si está familiarizado
con la estructura del directorio de la aplicación web en un proyecto Maven, entonces sabrá que
src/main/webapp
es la ubicación predeterminada para la raíz de su WAR. Si necesita anular este valor
predeterminado, puede especificar una ruta alternativa en la anotación @WebAppConfiguration
(por
ejemplo, @WebAppConfiguration("src/test/webapp")
). Si necesita hacer referencia a la ruta del recurso
principal desde la ruta de clases en lugar de desde el sistema de archivos, puede usar el prefijo
classpath:
de Spring.
Tenga en cuenta que el soporte de pruebas es para el de implementaciones el
WebApplicationContext
en Spring está a la par con el soporte para implementaciones
estándar
de ApplicationContext
. Al realizar pruebas con WebApplicationContext
, puede declarar
libremente archivos de configuración XML, scripts Groovy o clases marcadas con la anotación
@Configuration
utilizando la anotación @ContextConfiguration
. También puede utilizar
libremente cualquier otra anotación de prueba, como @ActiveProfiles
,
@TestExecutionListeners
, @Sql
, @Rollback
. y otros.
Los ejemplos
restantes de esta sección demuestran algunas de las diferentes opciones de configuración para cargar WebApplicationContext
.
El siguiente ejemplo muestra el soporte del marco TestContext para la convención sobre la configuración:
@ExtendWith(SpringExtension.class)
// predeterminado "file:src/main/webapp" "
@WebAppConfiguration
// detecta "WacTests-context.xml" in the same package
// o clases anidadas estáticas con la anotación @Configuration
@ContextConfiguration
class WacTests {
//...
}
@ExtendWith(SpringExtension::class)
// por defecto es "file:src/main/webapp"
@WebAppConfiguration
// detecta "WacTests-context. xml" in the same package
// o clases anidadas estáticas con la anotación @Configuration
@ContextConfiguration
class WacTests {
//...
}
If Si marca la clase de prueba usando la anotación @WebAppConfiguration
sin especificar la ruta a
la base de datos de recursos, entonces la ruta del recurso realmente toma el valor predeterminado file:src/main/webapp
.
Del mismo modo, si declara una anotación @ContextConfiguration
sin especificar los recursos locations
,
las clases
del componente o los contextos de inicializadores
, entonces Spring intenta
detectar la presencia de su configuración usando convenciones (es decir, WacTests-context.xml
en el
mismo paquete que la clase WacTests
o clases anidadas estáticas anotadas con
@Configuration
).
El siguiente ejemplo muestra cómo declarar explícitamente la ruta a una base
de recursos usando la anotación @WebAppConfiguration
y la ubicación de los recursos XML usando el
anotación
@ContextConfiguration
:
@ExtendWith(SpringExtension.class)
// recurso del sistema de archivos
@WebAppConfiguration("webapp")
// recurso classpath
@ContextConfiguration(" /spring/test-servlet-config.xml")
class WacTests {
//...
}
@ExtendWith(SpringExtension::class)
// recurso del sistema de archivos
@WebAppConfiguration("webapp")
// recurso classpath
@ContextConfiguration("/spring/test-servlet-config.xml")
class WacTests {
//...
}
Es importante tener en cuenta aquí la diferente semántica de las rutas con estas dos anotaciones. De forma
predeterminada, las rutas de recursos anotadas con @WebAppConfiguration
se basan en el sistema de
archivos, mientras que las ubicaciones de recursos anotadas con @ContextConfiguration
se basan en
classpath.
El siguiente ejemplo muestra que podemos anular la semántica de recursos predeterminada para ambas anotaciones especificando un prefijo de recurso Spring:
@ExtendWith(SpringExtension.class)
// recurso classpath
@WebAppConfiguration("classpath:test-web-resources")
// recurso del sistema de archivos
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
clase WacTests {
//...
}
@ExtendWith(SpringExtension::class)
// recurso de ruta de clase
@WebAppConfiguration("classpath:test-recursos web")
// recurso del sistema de archivos
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
clase WacTests {
//...
}
Compare los comentarios de este ejemplo con el anterior.
Para brindar soporte integral para las pruebas web, el marco TestContext tiene un
ServletTestExecutionListener
que está habilitado de forma predeterminada. Al realizar pruebas en WebApplicationContext
,
este oyente es TestExecutionListener
establece el estado local del hilo predeterminado usando RequestContextHolder
de Spring Web
antes de cada método de prueba y crea un MockHttpServletRequest
, MockHttpServletResponse
y
ServletWebRequest
basado en la ruta de recurso principal configurada con
@WebAppConfiguration
. ServletTestExecutionListener
también garantiza que MockHttpServletResponse
y ServletWebRequest
puedan inyectarse en la instancia de prueba y, cuando se complete la prueba, limpia
el estado local del subproceso. .
Una vez que haya cargado el WebApplicationContext
para su
prueba, es posible que necesite interactuar con simulaciones web, por ejemplo, para configurar un banco de pruebas o
ejecutar aserciones. después de llamar a su componente web. El siguiente ejemplo muestra qué objetos simulados se
pueden descubrir y asociar automáticamente con su instancia de prueba. Tenga en cuenta que WebApplicationContext
y MockServletContext
se almacenan en caché para todo el conjunto de pruebas, mientras que cada método
de prueba administra otros objetos simulados mediante ServletTestExecutionListener
. ServletTestExecutionListener
.
@SpringJUnitWebConfig
class WacTests {
@Autowired
WebApplicationContext wac; // en caché
@Autowired
MockServletContext servletContext; // en caché
@Autowired
MockHttpSession session;
@Autowired
MockHttpServletRequest request;
@Autowired
MockHttpServletResponse response;
@Autowired
ServletWebRequest webRequest;
//...
}
@SpringJUnitWebConfig
class WacTests {
@Autowired
lateinit var wac: WebApplicationContext // en caché
@Autowired
lateinit var servletContext: MockServletContext // en caché
@Autowired
lateinit var session: MockHttpSession
@Autowired
lateinit var request: MockHttpServletRequest
@Autowired
lateinit var response: MockHttpServletResponse
@Autowired
lateinit var webRequest: ServletWebRequest
//...
}
div>
Contexto de almacenamiento en caché
Una vez que el marco TestContext ha cargado
el ApplicationContext
(o WebApplicationContext
) para una prueba, este contexto se
almacena en caché y se reutiliza para todas las pruebas posteriores que declaran la misma configuración de
contexto
única dentro del mismo conjunto de pruebas. Para comprender cómo funciona el almacenamiento en caché, es
importante
comprender qué se entiende por "único" y "conjunto de pruebas".
ApplicationContext
se puede
identificar de forma única mediante la combinación de parámetros de configuración que Se utiliza para cargarlo.
Por
lo tanto, se utiliza una combinación única de parámetros de configuración para generar la clave bajo la cual se
almacena en caché el contexto. El marco TestContext utiliza los siguientes parámetros de configuración para
construir la clave de caché de contexto:
locations
(con@ContextConfiguration
)clases
(con@ContextConfiguration
)contextInitializerClasses
(de@ContextConfiguration
)contextCustomizers
(deContextCustomizerFactory
) - esto incluye métodos, marcados con la anotación@DynamicPropertySource
, así como varias funciones de las herramientas de soporte de prueba Spring Boot, como@MockBean
y@SpyBean
anotaciones.contextLoader
(con@ContextConfiguration
)padre
(con@ContextHierarchy
)activeProfiles
(con@ActiveProfiles
)propertySourceLocations
(con@TestPropertySource
)propertySourceProperties
(con@TestPropertySource
)resourceBasePath
(con@WebAppConfiguration
)
Por ejemplo, si TestClassA
especifica {"app-config.xml", " test-config.xml".}
para un
atributo locations
(o value
) anotado con @ContextConfiguration
, se carga
el
marco TestContext el ApplicationContext
correspondiente y lo guarda en una caché de contexto de
clave
estática que se basa únicamente en estas ubicaciones. Entonces, si TestClassB
también define {"app-config.xml",
"test-config.xml"}
para sus ubicaciones (explícita o implícitamente mediante herencia), pero no
define
una anotación @WebAppConfiguration
, un ContextLoader
diferente, diferentes perfiles
activos, diferentes inicializadores de contexto, diferentes fuentes de propiedades de prueba o un contexto
principal
diferente, entonces el mismo ApplicationContext
se comparte entre ambas clases de prueba. Esto
significa que los recursos se gastan en cargar el contexto de la aplicación solo una vez (para cada conjunto de
pruebas) y las ejecuciones de pruebas posteriores son mucho más rápidas.
El marco TestContext de Spring almacena contextos de aplicaciones en un caché estático. Esto significa que el
contexto se almacena literalmente en una variable static
. En otras palabras, si las pruebas se
ejecutan en procesos separados, el caché estático se borra entre cada ejecución de prueba, lo que
deshabilita
efectivamente el mecanismo de almacenamiento en caché.
Para aprovechar el mecanismo de almacenamiento en caché, todas las pruebas deben ejecutarse dentro del
proceso.
mismo proceso o conjunto de pruebas. Esto se puede lograr ejecutando todas las pruebas como un grupo en el
IDE.
Del mismo modo, cuando se ejecutan pruebas utilizando un marco de compilación como Ant, Maven o Gradle, es
importante asegurarse de que el marco de compilación no se bifurque entre pruebas. Por ejemplo, si forkMode
para el complemento Maven Surefire está configurado en
always
o pertest
, el marco TestContext no podrá almacenar en caché los contextos
de la aplicación entre clases de prueba, lo que resultará en una compilación significativamente más lenta.
proceso.
El tamaño de la caché de contexto es limitado, con un tamaño máximo predeterminado de
32. Cuando se alcanza el tamaño máximo, se utiliza una política de desalojo de tiempo de uso (LRU). desalojar y
cerrar contextos obsoletos. Puede configurar el tamaño máximo desde la línea de comandos o crear un script
estableciendo una propiedad del sistema JVM llamada spring.test.context.cache.maxSize
.
Alternativamente, puede establecer la misma propiedad a través de SpringProperties
mecanismo.
Debido a que la gran cantidad de contextos de aplicaciones cargados en un conjunto de pruebas
competitivo puede hacer que el conjunto demore demasiado en ejecutarse, a menudo es útil saber exactamente
cuántos
contextos se han cargado y almacenado en caché. . Para ver estadísticas del caché de contexto subyacente, puede
establecer el nivel de registro para la categoría de registro
org.springframework.test.context.cache
en
DEBUG
.
En el improbable caso de que una prueba corrompa el contexto de la aplicación y requiera una recarga (por
ejemplo,
cambiando una definición de bean o el estado de un objeto de la aplicación), puede anotar su clase de prueba o
método de prueba con
@DirtiesContext
(consulte la descripción de la anotación).@DirtiesContext
en la
sección
"Anotaciones
para pruebas en Spring"). Esto le indica a Spring que elimine el contexto del caché y reconstruya el
contexto
de la aplicación antes de ejecutar la siguiente prueba que requiera el mismo contexto de la aplicación. Tenga en
cuenta que la compatibilidad con la anotación @DirtiesContext
la proporcionan los oyentes DirtiesContextBeforeModesTestExecutionListener
y DirtiesContextTestExecutionListener
, que están habilitados de forma predeterminada.
Si necesita depurar una ejecución de prueba utilizando Spring TestContext Framework, puede resultar útil
analizar la salida de la consola (es decir, la salida a las secuencias SYSOUT
y
SYSERR
).
Algunas herramientas de compilación integradas e IDE pueden asociar la salida de la consola con una prueba
específica; sin embargo, no podrá asociar fácilmente algunos resultados de la consola con una prueba
específica.
Con respecto a los resultados de la consola activados por el propio Spring Framework o por componentes
registrados en ApplicationContext
, es importante comprender el ciclo de vida del
ApplicationContext
que fue cargado por Spring TestContext Framework en el conjunto de pruebas.
El ApplicationContext
para una prueba normalmente se carga al preparar una instancia de la clase
de
prueba, por ejemplo, para realizar la inyección de dependencia en campos de instancia de prueba, marcados
con la
anotación @Autowired
. Esto significa que cualquier salida de consola activada durante la
inicialización de ApplicationContext
normalmente no se puede asociar con un método de prueba
independiente. Sin embargo, si el contexto se cierra inmediatamente antes de que se ejecute el método de
prueba
de acuerdo con la semántica de la anotación @DirtiesContext
,
se cargará una nueva instancia de contexto inmediatamente antes de que se ejecute el método de prueba. En el
último escenario, el IDE o la herramienta de compilación incorporada podrían
potencialmente asociar la salida de la consola con un método de prueba independiente.
ApplicationContext
para una prueba podría cerrarse en uno de los siguientes escenarios.
El contexto se cierra según el semántica de la anotación
@DirtiesContext
.El contexto está cerrado porque se eliminó automáticamente del caché de acuerdo con la política de desalojo de LRU.
El contexto se cierra a través del gancho de terminación de JVM cuando finaliza la JVM para el conjunto de pruebas.
Si el contexto se cierra de acuerdo Según la semántica de la anotación @DirtiesContext
después
de un
método de prueba particular, el IDE o la herramienta de compilación incorporada podrían potencialmente
asociar
la salida de la consola con el método de prueba individual. Si el contexto se cierra de acuerdo con la
semántica
@DirtiesContext
después de ejecutar la clase de prueba, cualquier salida de la consola que se
active mientras ApplicationContext
está cerrado no se puede asociar con un método de prueba
separado. Además, cualquier salida de la consola activada durante la fase de apagado a través de un enlace
de
apagado de la JVM no se puede asociar con un método de prueba separado.
Si el ApplicationContext
de Spring se cierra mediante un apagado El gancho de las devoluciones
de
llamada de JVM ejecutadas durante la fase de apagado se ejecuta en un hilo llamado
SpringContextShutdownHook
.
Por lo tanto, si desea deshabilitar la salida de la consola activada al cerrar
ApplicationContext
a
través del enlace de cierre de la JVM, puede registrar un filtro personalizado con el marco de registro para
ignorar cualquier registro iniciado por ese subproceso.
Jerarquías de contexto
Al escribir pruebas de integración que utilizan un ApplicationContext
cargado desde Spring, a
menudo puede ser suficiente probar en un contexto. Sin embargo, hay ocasiones en las que es útil o incluso
necesario realizar pruebas con una jerarquía de instancias de ApplicationContext
. Por ejemplo,
si
está desarrollando una aplicación web en Spring MVC, normalmente tendrá un
WebApplicationContext
raíz cargado por un ContextLoaderListener
de Spring y un secundario
WebApplicationContextl
, cargado por DispatcherServlet
desde Spring. Esto da como
resultado una jerarquía de
contexto padre-hijo donde los componentes y la configuración de la infraestructura común se declaran en el
contexto raíz y son consumidos en el contexto hijo por componentes específicos de la web. Otro caso de uso
ocurre en las aplicaciones Spring Batch, donde a menudo hay un contexto principal que proporciona
configuración para la infraestructura general por lotes y un contexto secundario para la configuración de un
trabajo por lotes específico.
Puede escribir pruebas de integración que use jerarquías de contextos declarando una configuración de
contexto
usando la anotación @ContextHierarchy
, ya sea para una clase de prueba separada o dentro de una
jerarquía de clases de prueba. Si se declara una jerarquía de contexto para varias clases en la jerarquía de
clases de prueba, también es posible combinar o anular la configuración de contexto para un nivel con nombre
específico en la jerarquía de contexto. Al fusionar la configuración para un nivel determinado de la
jerarquía,
el tipo de recursos de configuración (es decir, archivos de configuración XML o clases de componentes) debe
ser
consistente. De lo contrario, es perfectamente aceptable tener diferentes niveles en la jerarquía de
contexto
configurados usando diferentes tipos de recursos.
Los ejemplos restantes basados en JUnit Jupiter en esta sección ilustran escenarios de configuración comunes para pruebas de integración que requieren el uso de jerarquías de contexto.
ControllerIntegrationTests
representa un escenario típico de prueba de integración para una
aplicación web en Spring MVC, declarando una jerarquía de contextos que consta de dos niveles; uno para el
WebApplicationContext
raíz (cargado usando la clase TestAppConfig @Configuration) y el otro
para el servlet despachador WebApplicationContext (cargado usando la clase WebConfig @Configuration). El
WebApplicationContext que se descubre y asocia automáticamente con la instancia de prueba es el contexto
para el contexto secundario (es decir, el contexto de nivel más bajo en la jerarquía). El siguiente listado
muestra este escenario de configuración:
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = TestAppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {
@Autowired
WebApplicationContext wac;
// ...
}
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextHierarchy(
ContextConfiguration(classes = [TestAppConfig::class]),
ContextConfiguration(classes = [WebConfig::class]))
class ControllerIntegrationTests {
@Autowired
lateinit var wac: WebApplicationContext
// ...
}}
Las clases de prueba en este ejemplo definen una jerarquía de contextos dentro de una jerarquía de clases de
prueba. AbstractWebTests
declara la configuración para la raíz
WebApplicationContext
en una aplicación web basada en Spring. Sin embargo, tenga en cuenta que
AbstractWebTests
no declara la anotación @ContextHierarchy
. Por lo tanto, las
subclases AbstractWebTests
pueden participar opcionalmente en la jerarquía de contexto o seguir
la semántica de anotación estándar @ContextConfiguration
. SoapWebServiceTests
y
RestWebServiceTests
amplían AbstractWebTests
y definen una jerarquía de contextos
utilizando la anotación @ContextHierarchy
. Como resultado, se cargan tres contextos de
aplicación (uno para cada declaración de anotación @ContextConfiguration
) y el contexto de
aplicación cargado según la configuración en AbstractWebTests
se establece como contexto
principal. Para cada contexto cargado para subclases específicas. El siguiente listado muestra este
escenario de configuración:
@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 {}
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
abstract class AbstractWebTests
@ContextHierarchy(ContextConfiguration("/spring/soap-ws-config.xml"))
class SoapWebServiceTests : AbstractWebTests()
@ContextHierarchy(ContextConfiguration("/spring/rest-ws-config.xml"))
class RestWebServiceTests : AbstractWebTests()
Las clases en este ejemplo demuestran el uso de niveles de jerarquía con nombre para agregar configuración
para niveles específicos en una jerarquía de contexto. BaseTests
define dos niveles en la
jerarquía, a saber, parent
y child
. ExtendedTests
extiende
BaseTests
e indica a Spring TestContext Framework que agrupe la configuración de contexto para el nivel
child
de la jerarquía, asegurando que los nombres declarados en el El atributo de
nombre
en la anotación @ContextConfiguration
era child
. Esto carga
tres contextos de aplicación: uno para /app-config.xml
, uno para /user-config.xml
y uno para {"/user-config .xml", "/order-config.xml"}
. Como en el ejemplo anterior, el contexto
de la aplicación cargado desde /app-config.xml
se establece como el contexto principal de los
contextos cargados desde /user-config.xml
y {"/user-config.xml",
"/order-config.xml"}
. El siguiente listado muestra este escenario de configuración:
@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 {}
@ExtendWith(SpringExtension::class)
@ContextHierarchy(
ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
open class BaseTests {}
@ContextHierarchy(
ContextConfiguration(name = "child", locations = ["/order-config.xml"])
)
class ExtendedTests : BaseTests() {}
A diferencia del ejemplo anterior, este ejemplo demuestra cómo anular la configuración para un nivel con
nombre determinado en la jerarquía de contexto estableciendo el indicador inheritLocations
en
la anotación @ContextConfiguration
en
false
. Por lo tanto, el contexto de la aplicación para ExtendedTests
se carga solo
desde /test-user-config.xml
,
y su padre es el contexto cargado desde /app-config. xml
. El siguiente listado muestra este
escenario de configuración:
@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 {}
@ExtendWith(SpringExtension::class)
@ContextHierarchy(
ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
open class BaseTests {}
@ContextHierarchy(
ContextConfiguration(
name = "child",
locations = ["/test-user-config.xml"],
inheritLocations = false
))
class ExtendedTests : BaseTests() {}
@DirtiesContext
en una prueba cuyo
contexto está configurado como parte de una jerarquía de contexto, puede usar la anotación
hierarchyMode
flag para controlar el borrado del caché de contexto. Para obtener más
información,
consulte la descripción de la
anotación @DirtiesContext
en la sección "Anotaciones
para pruebas en Spring" y javadoc por anotación @DirtiesContext
.
GO TO FULL VERSION