CodeGym /Cursos Java /Módulo 5. Spring /Gestión de contexto

Gestión de contexto

Módulo 5. Spring
Nivel 11 , Lección 6
Disponible

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.

ApplicationContext marcado con la anotación @Autowired

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:

Java

@SpringJUnitConfig
class MyTest {
    @Autowired 
    ApplicationContext applicationContext;
    // cuerpo de la clase...
}
  1. Inyectar ApplicationContext.
Kotlin

@SpringJUnitConfig
class MyTest {
    @Autowired 
    lateinit var applicationContext: ApplicationContext
    // cuerpo de la clase...
}
  1. 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:

Java

@SpringJUnitWebConfig 
class MyWebAppTest {
    @Autowired 
    WebApplicationContext wac;
    // cuerpo de la clase...
}
  1. Configurar WebApplicationContext.
  2. Inyectar WebApplicationContext.
Kotlin

@SpringJUnitWebConfig 
class MyWebAppTest {
    @Autowired 
    lateinit var wac: WebApplicationContext
    // cuerpo de la clase...
}
  1. Configurar WebApplicationContext.
  2. 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.

Java

@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...
}}
  1. Establezca el atributo de ubicación en la lista de archivos XML.
Kotlin

@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...
}
  1. 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:

Java

@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"}) 
class MyTest {
    // cuerpo de clase...
}
  1. Especifique archivos XML sin utilizar el atributo location.
Kotlin

@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-config.xml") 
class MyTest {
    // cuerpo de clase...
}
  1. Especificar archivos XML sin usar el locationde 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:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration 
class MyTest {
    // cuerpo de clase...
}}
  1. Cargue la configuración desde la ubicación predeterminada.
Kotlin
 
@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration 
class MyTest {
    // cuerpo de clase...
}
  1. 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.

Activación soporte para scripts Groovy
El soporte para usar scripts Groovy para cargar 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:

Java

@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...
}
Kotlin

@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...
}
  1. 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:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde
// "classpath:com/ example/MyTestContext.groovy"
@ContextConfiguration 
class MyTest {
    // cuerpo de clase...
}
  1. Cargue la configuración desde la ubicación predeterminada.
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration 
class MyTest {
    // cuerpo de clase...
}
  1. Carga la configuración desde ubicación predeterminada.
Declaración de configuración XML y scripts Groovy al mismo tiempo

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:

Java

@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...
}
Kotlin

@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:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext se cargará desde AppConfig y TestConfig
@ContextConfiguration(classes = { AppConfig.class, TestConfig.class}) 
class MyTest {
    // cuerpo de clase...
}
  1. Especificar clases de componentes.
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext se cargará desde AppConfig y TestConfig
@ContextConfiguration(classes = [AppConfig::class, TestConfig::class])  
class MyTest {
    // cuerpo de clase...
}
  1. Especificar componente clases.
Clases de componentes

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:

Java

@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
    }
}
  1. Cargando información de configuración desde un archivo anidado clase Config.
Kotlin

@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
    }
}
  1. 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:

Java

@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...
} 
  1. Establecer la configuración utilizando una clase de configuración y un inicializador.
Kotlin

@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...
}
  1. 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:

Java

@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...
}
  1. Establezca la configuración usando un solo inicializador.
Kotlin

@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...
}
  1. 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.

A partir de Spring Framework 5.3, la configuración de prueba también se puede heredar de las clases adjuntas. Consulte "Configuración de clase de prueba con anotación " 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:

Java

@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...
}
  1. Archivo de configuración definido en la superclase.
  2. Archivo de configuración definido en la subclase.
Kotlin

@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...
}
  1. Archivo de configuración definido en la superclase.
  2. 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:

Java

// 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...
}
  1. Clase de configuración definida en una superclase.
  2. Clase de configuración definida en una subclase.
Kotlin

// 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...
}
  1. Configuración clase, definida en la superclase.
  2. 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:

Java

// 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...
}
  1. Inicializador definido en la superclase.
  2. Inicializador definido en una subclase.
Kotlin

// 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...
}
  1. Inicializador definido en la superclase.
  2. 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.

Puede utilizar la anotación @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>
Java

@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
    }
}
Kotlin

@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:

Java

@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();
    }
}
Kotlin

@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()
    }
}
Java

@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");
    }
}
Kotlin

@Configuration
@Profile("production")
class JndiDataConfig {
    @Bean(destroyMethod = "")
    fun dataSource(): DataSource {
        val ctx = InitialContext()
        return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
    }
}
Java

@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();
    }
}
Kotlin

@Configuration
@Profile("default")
class DefaultDataConfig {
    @Bean
    fun dataSource(): DataSource {
        return EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .build()
    }
}
Java

@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();
    }
}
Kotlin

@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()
    }
}
Java

@SpringJUnitConfig({
        TransferServiceConfig.class,
        StandaloneDataConfig.class,
        JndiDataConfig.class,
        DefaultDataConfig.class})
@ActiveProfiles("dev")
class TransferServiceTest {
    @Autowired
    TransferService transferService;
    @Test
    void testTransferService() {
        // prueba transferService
    }
}
Kotlin

@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: obtiene dataSource mediante inyección de dependencia utilizando la anotación @Autowired.

  • StandaloneDataConfig : Define dataSource para una base de datos integrada, adecuada para pruebas de desarrollador.

  • JndiDataConfig: Define dataSource que se recuperó de JNDI en producción.

  • DefaultDataConfig: define el dataSource 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:

A partir de Spring Framework 5.3, la configuración de prueba también puede heredar de las clases adjuntas. Consulte "Configuración de clase de prueba con anotación @Nested para obtener más detalles" .
Java

@SpringJUnitConfig({
        TransferServiceConfig.class,
        StandaloneDataConfig.class,
        JndiDataConfig.class,
        DefaultDataConfig.class})
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
Kotlin

@SpringJUnitConfig(
        TransferServiceConfig::class,
        StandaloneDataConfig::class,
        JndiDataConfig::class,
        DefaultDataConfig::class)
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
Java

// perfil "dev" hereda de la superclase
class TransferServiceTest extends AbstractIntegrationTest {
    @Autowired
    TransferService transferService;
    @Test
    void testTransferService() {
        // prueba transferService
    }
}
Kotlin

// 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:

Java

// perfil "dev" se reemplaza por "producción"
@ActiveProfiles(profiles = "production", inheritProfiles = false)
class ProductionTransferServiceTest extends AbstractIntegrationTest {
    // test body
}
Kotlin

// 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:

Java

// el perfil "dev" se anula mediante programación usando un solucionador personalizado
@ActiveProfiles(
       resolver = OperatingSystemActiveProfilesResolver.class,
       inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
    // test body
}
Kotlin

// el perfil "dev" se anula mediante programación utilizando un solucionador personalizado
@ActiveProfiles(
       resolver = OperatingSystemActiveProfilesResolver::class,
       inheritProfiles = false)
class TransferServiceTest : AbstractIntegrationTest() {
    // test body
}
Java

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};
    }
}
Kotlin

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:

Java

@ContextConfiguration
@TestPropertySource("/test.properties") 
class MyIntegrationTests {
    // cuerpo de clase...
}
  1. Establezca el archivo de propiedades con una ruta absoluta.
Kotlin

@ContextConfiguration
@TestPropertySource("/test.properties" ) 
class MyIntegrationTests {
    // cuerpo de clase...
}
  1. 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:

Java

@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"}) 
class MyIntegrationTests {
    // cuerpo de la clase...
}
  1. Establezca dos propiedades usando dos sintaxis clave-valor.
Kotlin

@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) 
class MyIntegrationTests {
    // cuerpo de clase...
} 
  1. 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:

Java

@ContextConfiguration
@TestPropertySource(
    locations = "/test.properties",
    properties = {"timezone = GMT", "port: 4242"}
)
class MyIntegrationTests {
    // cuerpo de la clase...
}
Kotlin

@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.

A partir de Spring Framework 5.3, la configuración de prueba también puede heredar de las clases adjuntas. Consulte "Configuración de clase de prueba con anotación "@Nestedpara 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:

Java

@TestPropertySource ("base.properties")
@ContextConfiguration
class BaseTest {
    // ...
}
@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
    // ...
}
Kotlin

@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:

Java

@TestPropertySource(properties = "key1 = value1 ")
@ContextConfiguration
class BaseTest {
    // ...
}
@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
    // ...
}
Kotlin

@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.

Java

@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...
}
Kotlin

@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:

Java

@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 {
    //...
}
Kotlin

@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:

Java

@ExtendWith(SpringExtension.class)
// recurso del sistema de archivos
@WebAppConfiguration("webapp")
// recurso classpath
@ContextConfiguration(" /spring/test-servlet-config.xml")
class WacTests {
    //...
}
Kotlin

@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:

Java

@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 {
    //...
}
Kotlin

@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.

Trabajar con simulacros web

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.

Java

@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;
    //...
}
Kotlin

@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 (de ContextCustomizerFactory) - 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.

Suites de pruebas y procesos bifurcados

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.

Ciclo de vida de ApplicationContext y salida de consola

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.

La única clase de prueba con una jerarquía 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:

Java
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
    @ContextConfiguration(classes = TestAppConfig.class),
    @ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {
    @Autowired
    WebApplicationContext wac;
    // ...
}
Kotlin
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextHierarchy(
    ContextConfiguration(classes = [TestAppConfig::class]),
    ContextConfiguration(classes = [WebConfig::class]))
class ControllerIntegrationTests {
    @Autowired
    lateinit var wac: WebApplicationContext
    // ...
}}
Jerarquía de clases con contexto principal implícito

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:

Java
@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 {}
Kotlin
@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()
Jerarquía de clases con configuración de jerarquía de contexto combinada

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:

Java
@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 {}
Kotlin
@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() {}
Jerarquía de clases con configuración de jerarquía de contexto anulada

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:

Java
@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 {}
Kotlin
@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() {}
Si usa la anotación @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.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION