Al escribir pruebas de integración para una base de datos relacional, suele ser útil ejecutar scripts SQL para cambiar el esquema de la base de datos o insertar datos de prueba en tablas. El módulo spring-jdbc
proporciona soporte para inicializar una base de datos integrada o existente mediante la ejecución de scripts SQL cuando ApplicationContext
se carga en Spring. Para obtener más información, consulte "Soporte de base de datos integrada" y "Prueba de la lógica de acceso a datos utilizando la base de datos integrada".
Aunque es muy razonable inicializar el base de datos para probar una vez al cargar ApplicationContext
, a veces aún necesita poder cambiar la base de datos durante las pruebas de integración. Las siguientes secciones explican cómo ejecutar scripts SQL mediante programación y declaración durante las pruebas de integración.
Ejecución de scripts SQL mediante programación
Spring proporciona las siguientes herramientas para ejecutar scripts SQL mediante programación en pruebas de métodos de integración.
org.springframework.jdbc.datasource.init.ScriptUtils
org.springframework .jdbc.datasource.init.ResourceDatabasePopulator
org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests
ScriptUtils
proporciona una colección de métodos estáticos para trabajar con scripts SQL y está destinada principalmente para uso interno dentro del marco. Sin embargo, si necesita un control total sobre el análisis y la ejecución de scripts SQL, ScriptUtils
puede ser más adecuado que algunas de las otras alternativas que se describen a continuación. Para obtener más información, consulte javadoc en métodos individuales en ScriptUtils
.
ResourceDatabasePopulator
proporciona una API orientada a objetos para completar, inicializar o completar mediante programación. limpieza de la base de datos utilizando scripts SQL definidos en recursos externos. ResourceDatabasePopulator
proporciona opciones para configurar la codificación de caracteres, el separador de declaraciones, los delimitadores de comentarios y los indicadores de manejo de errores utilizados en el análisis y la ejecución de scripts. Cada uno de los parámetros de configuración tiene un valor predeterminado. Para obtener detalles sobre los valores predeterminados, consulte javadoc. Para ejecutar scripts configurados en ResourceDatabasePopulator
, puede llamar al método populate(Connection)
para ejecutar el populador en java.sql.Connection
, o el método execute(DataSource)
para ejecutar el módulo de relleno contra javax.sql.DataSource
. El siguiente ejemplo especifica secuencias de comandos SQL para el esquema de prueba y los datos de prueba, el delimitador de declaración se establece en @@
y las secuencias de comandos se ejecutan en la relación DataSource
:
@Test
void databaseTest() {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource("test-schema.sql"),
new ClassPathResource("test-data.sql"));
populator.setSeparator("@@");
populator.execute(this.dataSource);
// ejecuta el código usando el circuito de prueba y los datos
}
@Test
fun databaseTest() {
val populator = ResourceDatabasePopulator()
populator.addScripts(
ClassPathResource("test-schema.sql"),
ClassPathResource("test-data.sql"))
populator.setSeparator("@@")
populator.execute(dataSource)
// ejecutar el código, utilizando el esquema y los datos de prueba
}
Tenga en cuenta que ResourceDatabasePopulator
delega internamente a ScriptUtils
la autoridad para analizar y ejecutar scripts SQL. De manera similar, los métodos executeSqlScript(..)
en AbstractTransactionalJUnit4SpringContextTests
y AbstractTransactionalTestNGSpringContextTests
utilizan internamente ResourceDatabasePopulator
para ejecutar scripts SQL. Para obtener más información, consulte el Javadoc sobre los distintos métodos executeSqlScript(..)
.
Ejecución declarativa de scripts SQL utilizando @Sql anotación
Además de los mecanismos anteriores para ejecutar scripts SQL mediante programación, puede configurar scripts SQL de forma declarativa en Spring TestContext Framework. En particular, puede declarar una anotación @Sql
en una clase de prueba o método de prueba para configurar sentencias SQL individuales o rutas de recursos de script SQL que se ejecutarán en una base de datos determinada antes o después de la prueba del método de integración. La compatibilidad con la anotación @Sql
la proporciona el escucha SqlScriptsTestExecutionListener
, que está habilitado de forma predeterminada.
@Sql
en el nivel de método anulan de forma predeterminada las declaraciones de nivel de clase. Sin embargo, a partir de Spring Framework 5.2, esta lógica operativa se puede configurar para cada clase de prueba o cada método de prueba mediante la anotación
@SqlMergeMode
.
Semántica del recurso de ruta
Cada ruta es interpretado como Resource
de Spring. Una ruta simple (por ejemplo, "schema.sql"
) 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 considera un recurso de ruta de clase absoluta (por ejemplo, "/org/example/schema.sql"
). La ruta que hace referencia a la URL (por ejemplo, la ruta con el prefijo classpath:
, file:
, http:
) se carga utilizando el recurso especificado protocolo.
El siguiente ejemplo muestra cómo utilizar la anotación @Sql
a nivel de clase y a nivel de método en una clase de prueba de integración basada en JUnit Jupiter:
@SpringJUnitConfig
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
void emptySchemaTest() {
// ejecuta código que utiliza el esquema de prueba sin ningún datos de prueba
}
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
void userTest() {
// ejecutar código usando el esquema de prueba y los datos de prueba
}
}
@SpringJUnitConfig
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
fun emptySchemaTest() {
// ejecutar código que utiliza el esquema de prueba sin ningún dato de prueba
}
@Test
@Sql("/test-schema.sql", "/test-user-data.sql")
fun userTest() {
// ejecutar código que utiliza circuito de prueba y datos de prueba
}
}
Detección de secuencias de comandos predeterminada
Si no se especifican secuencias de comandos o sentencias SQL, se intenta determinar una Script default
dependiendo de dónde se declara la anotación @Sql
. Si no se puede determinar el valor predeterminado, se generará una excepción IllegalStateException
.
Declaración de nivel de clase: si la clase de prueba anotada es
com.ejemplo.MyTest
, entonces el script predeterminado correspondiente esclasspath:com/example/MyTest.sql
.Declaración a nivel de método: Si el método de prueba anotado se llama
testMethod()
y está definido en la clasecom.example.MyTest
, entonces el script correspondiente se llamaráclasspath:com /ejemplo/MiPrueba por defecto. testMethod.sql
.
Declaración de múltiples conjuntos de anotaciones @Sql
Si es necesario configure múltiples conjuntos de scripts SQL para una clase o método de prueba determinado, pero con diferentes configuraciones de sintaxis, diferentes reglas de manejo de errores o diferentes fases de ejecución para cada conjunto, puede declarar múltiples instancias de la anotación @Sql
. En Java 8, puede utilizar la anotación @Sql
como anotación repetida. Alternativamente, puede usar la anotación @SqlGroup
como un contenedor explícito para declarar múltiples instancias de la anotación @Sql
.
El siguiente ejemplo muestra cómo use la anotación @Sql
como una anotación repetida en Java 8:
@Test
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`"))
@Sql("/test-user-data.sql") void userTest() {
// ejecutar código usando el esquema de prueba y los datos de prueba
}
// Kotlin aún no admite anotaciones duplicadas con persistencia que no sea SOURCE
En el script presentado en el ejemplo anterior, el script test-schema.sql
utiliza una sintaxis diferente para comentarios de una sola línea.
El siguiente ejemplo es idéntico al anterior, excepto que las declaraciones de la anotación @Sql
se agrupan en la anotación @SqlGroup
. En Java 8 y superiores, el uso de la anotación @SqlGroup
es opcional, pero es posible que sea necesario utilizar la anotación @SqlGroup
para lograr compatibilidad con otros lenguajes JVM, como Kotlin.
@Test
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql(" /test-user-data.sql")
)}
void userTest() {
// ejecutar código utilizando el esquema de prueba y los datos de prueba
}
@Test
@SqlGroup(
Sql("/test-schema.sql", config = SqlConfig(commentPrefix = "`")),
Sql("/test-user-data.sql"))
fun userTest() {
// ejecutar código usando el circuito de prueba y los datos de prueba
}
Fases de ejecución del script
Los scripts SQL predeterminados se ejecutan antes del método de prueba correspondiente. Sin embargo, si necesita ejecutar un conjunto específico de scripts después de un método de prueba (por ejemplo, para limpiar el estado de la base de datos), puede usar el atributo executionPhase
en @Sql
. anotación, como se muestra en el siguiente ejemplo:
@Test
@Sql(
scripts = "create-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED)
)
@Sql(
scripts = "delete-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD
)
void userTest() {
// ejecuta el código que indica confirmar datos de prueba
// a la base de datos fuera de la transacción de prueba
}
@Test
@SqlGroup(
Sql("create-test-data.sql",
config = SqlConfig( transactionMode = ISOLATED)),
Sql("delete-test-data.sql",
config = SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD))
fun userTest() {
// ejecuta el código que indica capturar los datos de prueba
// en la base de datos fuera de la transacción de prueba
}
Tenga en cuenta que AISLADO
y AFTER_TEST_METHOD
se importan estáticamente desde Sql.TransactionMode
y Sql.ExecutionPhase
, respectivamente.
Configuración de script usando @SqlConfig
El análisis de scripts se puede configurar y el manejo de errores usando la anotación @SqlConfig
. Si @SqlConfig
se declara como una anotación de nivel de clase para una clase de prueba de integración, entonces sirve como una configuración global para todos los scripts SQL en la jerarquía de clases de prueba. Cuando se declara directamente utilizando el atributo config
de la anotación @Sql
, la anotación @SqlConfig
sirve como configuración local para los scripts SQL declarados en el anotación @Sql
. Cada atributo en la anotación @SqlConfig
tiene un valor predeterminado implícito, que está documentado en el javadoc del atributo correspondiente. Debido a las reglas definidas para los atributos de anotación en la Especificación del lenguaje Java, lamentablemente no es posible establecer un atributo de anotación en null
. Por lo tanto, para admitir la anulación de la configuración global heredada, los atributos de anotación @SqlConfig
tienen un valor predeterminado explícito de ""
(para cadenas), {}
(para matrices) o DEFAULT
(para enumeraciones). Este enfoque permite que las declaraciones de anotaciones locales @SqlConfig
anulen selectivamente atributos individuales de las declaraciones de anotaciones globales @SqlConfig
proporcionando un valor distinto de ""
, {}
o DEFAULT
. Los atributos globales de la anotación @SqlConfig
siempre se heredan a menos que los atributos locales de la anotación @SqlConfig
proporcionen un valor explícito distinto de ""
, {}
o DEFAULT
. Por lo tanto, la configuración local explícita tiene prioridad sobre la configuración global.
Las opciones de configuración proporcionadas por las anotaciones @Sql
y @SqlConfig
son equivalentes a las admitidas por ScriptUtils
y ResourceDatabasePopulator
, pero son un superconjunto de los proporcionados por el elemento de espacio de nombres XML <jdbc:initialize-database/>
. Para obtener más información, consulte el javadoc para atributos individuales en las anotaciones @Sql
y @SqlConfig
.
Gestión de transacciones para anotación @Sql
De forma predeterminada, SqlScriptsTestExecutionListener
crea la semántica de transacción deseada para los scripts configurados con @Sql
anotación. Específicamente, los scripts SQL se ejecutan sin una transacción dentro de una transacción existente administrada por Spring (por ejemplo, una transacción administrada por TransactionalTestExecutionListener
para una prueba marcada con la anotación @Transactional
) o dentro de una transacción aislada, dependiendo del valor configurado del atributo transactionMode
en la anotación @SqlConfig
y de la presencia de un PlatformTransactionManager
en el ApplicationContext
prueba. Sin embargo, como mínimo, javax.sql.DataSource
debe estar presente en el ApplicationContext
de la prueba.
Si los algoritmos utilizados por SqlScriptsTestExecutionListener
para Si las definiciones de DataSource
y PlatformTransactionManager
y la salida de la semántica de la transacción no satisfacen sus necesidades, puede especificar nombres explícitos configurando dataSource
y transactionManager
atributos code en la anotación @SqlConfig
. También puede controlar la lógica de propagación de transacciones configurando el atributo transactionMode
en la anotación @SqlConfig
(por ejemplo, si los scripts deben ejecutarse en una transacción aislada). Aunque una discusión detallada de todas las opciones admitidas para administrar transacciones usando la anotación @Sql
está fuera del alcance de esta guía de referencia, el javadoc para @SqlConfig
anotación y oyente SqlScriptsTestExecutionListener
contienen información detallada y el siguiente ejemplo muestra un escenario de prueba típico usando JUnit Jupiter y pruebas transaccionales con la anotación @Sql
:
@SpringJUnitConfig(TestDatabaseConfig.class)
@Transactional
class TransactionalSqlScriptsTests {
final JdbcTemplate jdbcTemplate;
@Autowired
TransactionalSqlScriptsTests(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
@Sql("/test-data.sql")
void usersTest() {
// verifica el estado en la base de datos de prueba:
assertNumUsers(2);
// ejecutar código usando datos de prueba...
}
int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
void assertNumUsers(int expected) {
assertEquals(expected, countRowsInTable("user"),
"Number of rows in the [user] table.");
}
}
@SpringJUnitConfig(TestDatabaseConfig::class)
@Transactional
class TransactionalSqlScriptsTests @Autowired constructor(dataSource: DataSource) {
val jdbcTemplate: JdbcTemplate = JdbcTemplate(dataSource)
@Test
@Sql("/test-data.sql")
fun usersTest() {
// verifica el estado en la base de datos de prueba:
assertNumUsers(2)
// ejecuta el código, usando datos de prueba...
}
fun countRowsInTable(tableName: String): Int {
return JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName)
}
fun assertNumUsers(expected: Int) {
assertEquals(expected, countRowsInTable("user"),
"Number of rows in the [user] table.")
}
}
Tenga en cuenta que no es necesario borrar la base de datos después de ejecutar usersTest()
de método, ya que cualquier cambio realizado en la base de datos (ya sea en el método de prueba o en el script /test-data.sql
) se revierte automáticamente mediante el TransactionalTestExecutionListener
(ver más detalles en la sección sobre gestión de transacciones).
Fusionar y anular la configuración usando la anotación @SqlMergeMode
A partir de Spring Framework 5.2, puede fusionar declaraciones de anotaciones @Sql
a nivel de método con declaraciones a nivel de clase. Por ejemplo, esto le permitiría proporcionar configuración para un esquema de base de datos o algunos datos de prueba generales para una clase de prueba una vez y luego proporcionar datos de prueba adicionales específicos de cada caso para cada método de prueba. Para habilitar la combinación de anotaciones @Sql
, anote su clase de prueba o método de prueba con la anotación @SqlMergeMode(MERGE)
. Para deshabilitar la combinación para un método de prueba específico (o una subclase de prueba específica), puede volver al modo predeterminado usando la anotación @SqlMergeMode(OVERRIDE)
. Consulte la sección de documentación en anotación @SqlMergeMode
para ejemplos e información más detallada.
GO TO FULL VERSION