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.