Runner de JUnit 4 en Spring

Spring TestContext Framework ofrece integración completa con JUnit 4 a través de un corredor dedicado (compatible con JUnit 4.12 o superior). Al marcar las clases de prueba con la anotación @RunWith(SpringJUnit4ClassRunner.class), o la anotación más corta @RunWith(SpringRunner.class), los desarrolladores pueden implementar pruebas unitarias y de integración estándar basadas en JUnit 4 mientras aprovechan los beneficios del marco TestContext, como soporte para cargar contextos de aplicaciones, inyección de dependencia en instancias de prueba, ejecución transaccional de métodos de prueba, etc. Si necesita utilizar Spring TestContext Framework con un ejecutor alternativo (como el ejecutor Parameterized de JUnit 4) o ejecutores de terceros (como MockitoJUnitRunner), puede opcionalmente, use las funciones de soporte de reglas JUnit de Spring.

La siguiente lista de código muestra los requisitos mínimos para configurar una clase de prueba para ejecutar usando un Runner personalizado en Spring:

Java

@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
    @Test
    public void testMethod() {
        // lógica de prueba...
    }
}
Kotlin

@RunWith(SpringRunner::class)
@TestExecutionListeners
class SimpleTest {
    @Test
    fun testMethod() {
        // lógica de prueba...
    }
}

En el ejemplo anterior, la anotación @TestExecutionListeners está configurada con una lista vacía para deshabilitar los oyentes de forma predeterminada que, de otro modo, requerirían configurar ApplicationContext a través de la anotación @ContextConfiguration.

Spring JUnit 4 reglas

El paquete org.springframework.test.context.junit4.rules proporciona las siguientes reglas de JUnit 4 (compatibles con JUnit 4.12 o superior):

  • SpringClassRule

  • SpringMethodRule

SpringClassRule es el TestRule de JUnit que admite funciones de Spring TestContext Framework a nivel de clase, y SpringMethodRule es el MethodRule de JUnit que admite características a nivel de instancia y método de Spring TestContext Framework.

A diferencia de SpringRunner, la ventaja del soporte JUnit basado en reglas de Spring es que es independiente de cualquier implementación de org.junit.runner.Runner y, por lo tanto, se puede combinar con corredores alternativos existentes (como Parameterized de JUnit 4) o corredores de terceros (como MockitoJUnitRunner).

Para admitir la funcionalidad completa del marco TestContext, debe combinar SpringClassRule con SpringMethodRule. El siguiente ejemplo muestra la forma correcta de declarar estas reglas en una prueba de integración:

Java

// Opcionalmente, especifique un corredor que no sea Spring's Runner a través de @RunWith(...)
@ContextConfiguration
public class IntegrationTest {
    @ClassRule
    public static final SpringClassRule springClassRule = new SpringClassRule();
    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();
    @Test
    public void testMethod() {
        // lógica de prueba...
    }
}
Kotlin

// Opcionalmente, configuramos un corredor que no es un Spring Runner a través de @RunWith(...)
@ContextConfiguration
class IntegrationTest {
    @Rule
    val springMethodRule = SpringMethodRule()
    @Test
    fun testMethod() {
        // lógica de prueba...
    }
    companion object {
        @ClassRule
        val springClassRule = SpringClassRule()
    }
}

Clases auxiliares de JUnit 4

El paquete org.springframework.test.context.junit4 proporciona las siguientes clases auxiliares para casos de prueba basados en JUnit 4 (compatibles con JUnit 4.12 o superior):

  • AbstractJUnit4SpringContextTests

  • AbstractTransactionalJUnit4SpringContextTests

AbstractJUnit4SpringContextTests es una clase de prueba base abstracta que integra Spring TestContext Framework con soporte explícito de prueba ApplicationContext en el framework JUnit 4. Si extiende AbstractJUnit4SpringContextTests, puede acceder a la variable de instancia protected de applicationContext, que se puede utilizar para realizar búsquedas explícitas de beans o para probar el estado del contexto en su conjunto.

AbstractTransactionalJUnit4SpringContextTests es una extensión transaccional abstracta de AbstractJUnit4SpringContextTests que agrega algunas características convenientes para acceder a JDBC. Esta clase espera que ApplicationContext tenga definidos un bean javax.sql.DataSource y un bean PlatformTransactionManager. Si extiende AbstractTransactionalJUnit4SpringContextTests, puede acceder a la variable de instancia protected jdbcTemplate, que puede usar para ejecutar sentencias SQL para consultar los datos de la base de datos. Puede utilizar dichas consultas para validar el estado de la base de datos antes y después de ejecutar el código de la aplicación asociado con la base de datos, y Spring garantiza que dichas consultas se ejecuten dentro de la misma transacción que el código de la aplicación. Cuando se utiliza junto con una herramienta ORM, se deben evitar los falsos positivos. Como se menciona en la sección Soporte de pruebas de JDBC, AbstractTransactionalJUnit4SpringContextTests también proporciona métodos auxiliares que delegan autoridad a los métodos en JdbcTestUtils utilizando el jdbcTemplate mencionado anteriormente. Además, AbstractTransactionalJUnit4SpringContextTests proporciona el método executeSqlScript(..) para ejecutar scripts SQL en el DataSource configurado.

Estas clases son ayudas de extensión. Si no necesita que sus clases de prueba estén vinculadas a una jerarquía de clases específica de Spring, puede configurar sus propias clases de prueba personalizadas usando la anotación @ RunWith(SpringRunner.class) o reglas JUnit para Spring.

SpringExtension para JUnit Jupiter

Spring TestContext Framework ofrece un marco completo integración con el marco de pruebas JUnit Jupiter introducido en JUnit 5. Al anotar las clases de prueba con @ExtendWith(SpringExtension.class), puede implementar pruebas unitarias y de integración estándar sobre JUnit Jupiter mientras aprovecha las ventajas de beneficios del marco TestContext, como soporte para cargar contextos de aplicaciones, inyección de dependencias en instancias de prueba, ejecución transaccional de métodos de prueba, etc.

Además, gracias a la rica extensión API en JUnit Jupiter, Spring proporciona la siguientes capacidades además de ese conjunto de características que Spring admite para JUnit 4 y TestNG:

  • Inyección de dependencia para constructores de prueba, métodos de prueba y métodos de devolución de llamada del ciclo de vida de prueba. Consulte Inyección de dependencia con SpringExtension para obtener más información.

  • Soporte completo para ejecución condicional de pruebasbasadas en expresiones SpEL, variables de entorno, propiedades del sistema, etc. Para obtener más información y ejemplos, consulte la documentación de las anotaciones @EnabledIf y @DisabledIf en "Anotaciones de JUnit Jupiter para pruebas en Spring".

  • Anotaciones compuestas especiales que combinan anotaciones de Spring y JUnit Jupiter. Para obtener más información, consulte los ejemplos de anotaciones @TransactionalDevTestConfig y @TransactionalIntegrationTest en "Soporte para meta anotaciones para pruebas".

La siguiente lista de código muestra cómo configurar una clase de prueba para usar SpringExtension en combinación con @ContextConfiguration:

Java

// Le ordenamos a JUnit Jupiter que extienda la prueba con soporte de Spring 
@ExtendWith(SpringExtension.class)
// Indique a Spring que cargue ApplicationContext desde TestConfig.class
@ContextConfiguration(classes = TestConfig.class)
class SimpleTests {
    @Test void testMethod() {
        // lógica de prueba...
    }
} 
Kotlin

// Le ordenamos a JUnit Jupiter que extienda la prueba con soporte de Spring.
@ExtendWith(SpringExtension::class)
// Indique a Spring que cargue ApplicationContext desde TestConfig.class
@ContextConfiguration(classes = [TestConfig::class])
class SimpleTests {
    @Test
    fun testMethod() {
        // lógica de prueba...
    }
} 

Dado que JUnit 5 también puede usar anotaciones como metaanotaciones, Spring proporciona anotaciones compuestas @SpringJUnitConfig y @SpringJUnitWebConfig para simplificar la configuración de la prueba ApplicationContext y JUnit Jupiter.

El siguiente ejemplo utiliza la anotación @SpringJUnitConfig para reducir la cantidad de configuración utilizada en el ejemplo anterior:

Java

// Le indica a Spring que registre SpringExtension con JUnit
// Jupiter y cargue ApplicationContext desde TestConfig.class
@SpringJUnitConfig(TestConfig.class)
class SimpleTests {
    @Test
    void testMethod() {
        // lógica de prueba...
    }
}
Kotlin
 
// Le indica a Spring que registre un SpringExtension con JUnit
// Jupiter y cargue el ApplicationContext desde TestConfig.class
@SpringJUnitConfig(TestConfig::class)
class SimpleTests {
    @Test
    fun testMethod() {
        // lógica de prueba...
    }
}

De manera similar, el siguiente ejemplo utiliza la anotación @SpringJUnitWebConfig para crear un WebApplicationContext para su uso. con JUnit Júpiter:

Java

// Le indica a Spring que registre SpringExtension con JUnit
// Jupiter y cargue WebApplicationContext desde TestWebConfig.class
@SpringJUnitWebConfig(TestWebConfig.class)
class SimpleWebTests {
    @Test
    void testMethod() {
        // lógica de prueba...
    }
}
Kotlin

// Le indica a Spring que registre SpringExtension con JUnit
// Jupiter y cargue WebApplicationContext desde TestWebConfig::class
@SpringJUnitWebConfig(TestWebConfig::class)
class SimpleWebTests {
    @Test
    fun testMethod() {
        // lógica de prueba...
    }
}

Para obtener más información, consulte la documentación de @SpringJUnitConfig y @SpringJUnitWebConfig en "Anotaciones de JUnit Jupiter para pruebas en Spring".

La inyección de dependencia utilizando SpringExtension

SpringExtension implementa la extensión API ParameterResolver de JUnit Jupiter, que permite a Spring proporcionar inyección de dependencia para constructores de pruebas, métodos de prueba y devolución de llamada del ciclo de vida de pruebas métodos.

En particular, SpringExtension puede inyectar dependencias de una prueba ApplicationContext en constructores de pruebas y métodos anotados con @BeforeAll. , @AfterAll, @BeforeEach, @AfterEach, @Test, @RepeatedTest , @ParameterizedTest y otras anotaciones.

Inyección de dependencia a través del constructor

Si un determinado parámetro en un constructor de clase de prueba de JUnit Jupiter es de tipo ApplicationContext (o un subtipo del mismo) o anotado o metaanotado con @Autowired, @Qualifier o @Value, Spring inyecta el valor de este parámetro utilizando el correspondiente bean o valor de la prueba ApplicationContext.

Spring también se puede configurar para detectar y vincular automáticamente todos los argumentos al constructor de una clase de prueba si el constructor se considera autoenlazable. . Un constructor se considera autowired si una de las siguientes condiciones es verdadera (en orden de precedencia).

  • El constructor está anotado con @Autowired.

  • La anotación @TestConstructor está presente o metapresente para una clase de prueba con el atributo autowireMode establecido en ALL.

  • El modo de enlace y detección del constructor de pruebas predeterminado se ha cambiado a ALL.

Más información sobre el uso de @TestConstructor anotación y para cambiar el modo de enlace y detección automática del Test Constructor global, consulte la sección sobre la anotación @TestConstructor .

Si el constructor de una clase de prueba se considera autoenlazable, Spring se encarga de la resolución de argumentos para todos los parámetros en el constructor. Por lo tanto, ningún otro ParameterResolver registrado con JUnit Jupiter puede resolver parámetros para dicho constructor.

Dependencias del constructor de inyección para clases de prueba no se puede usar junto con el soporte de anotación @TestInstance(PER_CLASS) de JUnit Jupiter si la anotación @DirtiesContext se usa para cerrar el código de prueba ApplicationContext antes o después de los métodos de prueba.

La razón es que la anotación @TestInstance(PER_CLASS) le dice a JUnit Jupiter que almacene en caché la instancia de prueba entre llamadas al método de prueba. Por lo tanto, la instancia de prueba conservará las referencias a los beans inyectados originalmente desde un ApplicationContext que se cerró posteriormente. Debido a que el constructor de la clase de prueba solo se llamará una vez en tales escenarios, la inyección de dependencia no se repetirá y las pruebas posteriores interactuarán con beans del ApplicationContext privado, lo que puede generar errores.

Para utilizar la anotación @DirtiesContext en el modo "antes del método de prueba" o "después del método de prueba" en combinación con la anotación @TestInstance(PER_CLASS), debe Es necesario configurar las dependencias de Spring para recibirlas inyectándolas a través de un campo o definidor para que puedan reinyectarse entre llamadas al método de prueba.

En el siguiente ejemplo, Spring inyecta OrderService bean del ApplicationContext cargado desde TestConfig.class en el constructor OrderServiceIntegrationTests.

Java

@SpringJUnitConfig(TestConfig .class)
class OrderServiceIntegrationTests {
    private final OrderService orderService;
    @Autowired OrderServiceIntegrationTests(OrderService orderService) {
        this.orderService = orderService;
    }
    // pruebas utilizando el OrderService incorporado
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests @Autowired constructor(private val orderService: OrderService){
    // pruebas que utilizan el OrderService integrado
}

Tenga en cuenta que esta función permite que las dependencias de prueba sean final y por lo tanto inmutable.

Si la propiedad spring.test.constructor.autowire.mode es all (ver @TestConstructor), puede omitir la declaración de la anotación @Autowired para el constructor del ejemplo anterior, lo que da como resultado lo siguiente.

Java
        
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
    private final OrderService orderService;
    OrderServiceIntegrationTests(OrderService orderService) {
        this.orderService = orderService;
    }
    // pruebas utilizando el OrderService incorporado
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests(val orderService:OrderService) {
    // pruebas utilizando el OrderService integrado
}
Inyección de dependencia mediante método

Si el parámetro de un método de prueba de JUnit Jupiter o un método de devolución de llamada Es vital que el bucle de prueba sea del tipo ApplicationContext (o un subtipo del mismo) o esté anotado o metaanotado con @Autowired, @Qualifier o anotaciones @Value, Spring inyecta el valor para ese parámetro en particular usando el bean correspondiente de la prueba ApplicationContext.

En el siguiente ejemplo, Spring inyecta el OrderService de ApplicationContext cargado desde TestConfig.class en el método de prueba deleteOrder():

Java

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
    @Test
    void deleteOrder(@Autowired OrderService orderService) {
        // use orderService del ApplicationContext de la prueba
    }
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests {
    @Test
    fun deleteOrder(@Autowired orderService: OrderService) {
        // usa orderService desde el ApplicationContext de la prueba
    }
} 

Gracias al sólido soporte ParameterResolver de JUnit Jupiter, se pueden crear múltiples dependencias. Inyectado en un método, no solo desde Spring, sino también desde JUnit Júpiter u otras extensiones de terceros.

El siguiente ejemplo muestra cómo hacer que Spring y JUnit Júpiter inyecten simultáneamente dependencias en placeOrderRepeatedly() método de prueba.

Java

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
    @RepeatedTest(10)
    void placeOrderRepeatedly(RepetitionInfo repeatedInfo,
            @Autowired OrderService orderService) {
        // use orderService del ApplicationContext de la prueba
        // y repetitionInfo de JUnit Jupiter
    }
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests {
    @RepeatedTest (10)
    fun placeOrderRepeatedly(repetitionInfo:RepetitionInfo, @Autowired orderService:OrderService) {
        // use orderService del ApplicationContext de la prueba
        // y repetitionInfo de JUnit Jupiter
    }
}

Tenga en cuenta que el uso de la anotación @RepeatedTest de JUnit Jupiter permite que el método de prueba acceda a RepetitionInfo.

Configuración de clase de prueba con @Nested

Spring TestContext Framework admite el uso de anotaciones relacionadas con pruebas para clases de prueba marcadas con la anotación @Nested, en JUnit Jupiter desde Spring Framework 5.0; sin embargo, antes de Spring Framework 5.3, las anotaciones de configuración de prueba a nivel de clase no se heredaban de las clases anidadas, como es el caso de las superclases.

Spring Framework 5.3 introdujo soporte con todas las funciones para heredar la configuración de la clase de prueba de las clases anidadas, y esta configuración se heredará de forma predeterminada. Para cambiar el modo INHERIT predeterminado al modo OVERRIDE, puede marcar una única clase de prueba anotada con @Nested con @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE). Se aplicará una declaración @NestedTestConfiguration explícita a la clase de prueba anotada, así como a todas sus subclases y clases anidadas. Por lo tanto, es posible anotar una clase de prueba de alto nivel con la anotación @NestedTestConfiguration, y esto se aplicará a todas sus clases de prueba anidadas de forma recursiva.

Para permitir a los equipos de desarrollo para cambiar el valor predeterminado a OVERRIDE - por ejemplo, para garantizar la compatibilidad con las versiones 5.0-5.2 de Spring Framework - el modo predeterminado se puede cambiar globalmente a través de una propiedad del sistema JVM o un spring.properties archivo en la raíz del classpath. Para obtener más información, consulte la nota "Cambiar el modo de herencia de configuración envolvente predeterminado".

Aunque el siguiente ejemplo "Hola mundo" es muy simplificado, muestra cómo declarar la configuración de una clase de alto nivel heredada por sus clases de prueba, marcada con la anotación @Nested. En este ejemplo particular, solo se hereda la clase de configuración TestConfig. Cada clase de prueba anidada expone su propio conjunto de perfiles activos, lo que da como resultado un ApplicationContext independiente para cada clase de prueba anidada (consulte para obtener más detalles "Contexto de almacenamiento en caché"). Consulte la lista de anotaciones admitidas para ver qué anotaciones se pueden heredar en las clases de prueba con el < annotation@Nested.

Java

@SpringJUnitConfig(TestConfig.class)
class GreetingServiceTests {
    @Nested
    @ActiveProfiles("lang_en")
    class EnglishGreetings {
            @Test
            void hello(@Autowired GreetingService service) {
                assertThat(service.greetWorld()).isEqualTo("Hello World");
            }
    }
    @Nested
    @ActiveProfiles("lang_de")
    class GermanGreetings {
        @Test
        void hello(@Autowired GreetingService service) {
            assertThat(service.greetWorld()).isEqualTo("Hallo Welt");
        }
    }
}
Kotlin

@SpringJUnitConfig(TestConfig::class)
class GreetingServiceTests {
    @Nested
    @ActiveProfiles("lang_en")
    inner class EnglishGreetings {
        @Test
        fun hello(@Autowired service:GreetingService) {
            assertThat(service.greetWorld()).isEqualTo("Hello World")
        }
    }
    @Nested
    @ActiveProfiles("lang_de")
    inner class GermanGreetings {
        @Test
        fun hello(@Autowired service:GreetingService) {
            assertThat(service.greetWorld()).isEqualTo("Hallo Welt")
        }
    }
}

Clases auxiliares de TestNG

El paquete org.springframework.test.context.testng proporciona las siguientes clases auxiliares para casos de prueba basados en TestNG:

  • AbstractTestNGSpringContextTests

  • AbstractTransactionalTestNGSpringContextTests

AbstractTestNGS SpringContextTests es una clase de prueba base abstracta que integra Spring TestContext Framework con soporte explícito para las pruebas de ApplicationContext en el marco TestNG. Si extiende AbstractTestNGSpringContextTests, puede acceder a una variable de instancia de applicationContext protegida que se puede utilizar para realizar una búsqueda explícita de BIN o para probar el estado del contexto en su conjunto.

AbstractTransactionalTestNGSpringContextTests es una extensión transaccional abstracta AbstractTestNGSpringContextTests que agrega algunas características convenientes para acceder a JDBC. Esta clase espera que ApplicationContext tenga definidos un bean javax.sql.DataSource y un bean PlatformTransactionManager. Si extiende AbstractTransactionalTestNGSpringContextTests, puede acceder a la variable de instancia protected jdbcTemplate, que puede usar para ejecutar sentencias SQL para consultar los datos de la base de datos. Puede utilizar dichas consultas para validar el estado de la base de datos antes y después de ejecutar el código de la aplicación asociado con la base de datos, y Spring garantiza que dichas consultas se ejecuten dentro de la misma transacción que el código de la aplicación. Cuando se utiliza junto con una herramienta ORM, se deben evitar los falsos positivos. Como se menciona en la sección Soporte de pruebas de JDBC, AbstractTransactionalTestNGSpringContextTests también proporciona métodos auxiliares que delegan autoridad a los métodos en JdbcTestUtils utilizando el jdbcTemplate antes mencionado. Además, AbstractTransactionalTestNGSpringContextTests proporciona el método executeSqlScript(..) para ejecutar scripts SQL en el DataSource configurado.

Estas clases son ayudas de extensión. Si no necesita que sus clases de prueba estén vinculadas a una jerarquía de clases específica de Spring, puede configurar sus propias clases de prueba personalizadas usando @ContextConfiguration, @TestExecutionListeners y etc., y también instrumentar manualmente su clase de prueba usando TestContextManager. Consulte el código fuente de AbstractTestNGSpringContextTests para ver un ejemplo de cómo puede instrumentar su clase de prueba.