Pasa tus tests de integración con base de datos a la velocidad de la luz con dbcp2 BasicDataSource. Si estás utilizando para conectar a la base de datos DriverManagerDataSource de Spring JDBC estás perdiendo tiempo y recursos de forma exponencial.
Índice de contenidos
- 1. Introducción
- 2. Entorno
- 3. DriverManagerDataSource de Spring JDBC
- 4. dbcp2 BasicDataSource de Apache
- 5. Conclusiones
1. Introducción
Puede que «Han Solo» haya recorrido el «Corredor Kessel» en menos de 12 segundos, pero nadie será más rápido que tú ejecutando los tests de integración contra la base de datos si sigues los pasos de este tutorial.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro Retina 15′ (2.3 Ghz Intel Core I7, 16GB DDR3).
- Sistema Operativo: Mac OS Sierra 10.12.5
- Entorno de desarrollo: Eclipse Oxygen
- Apache Maven 3.3.0
- Java 8
- Spring 4
3. DriverManagerDataSource de Spring JDBC
Es muy posible que hasta ahora usaras una clasecita que tienen en Spring JDBC (DriverManagerDataSource.java), muy utilizada por verse en muchos ejemplos de Internet, para conectar con tu base de datos, y ejecutar tus tests de integración, donde pruebas todas tus querys de forma transaccional para que no dependan unos tests de otros. Seguramente tus contextos de tests de Spring tengan una pinta parecida a lo siguiente:
applicationContext-test.xml
<bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@IP:PUERTO:NOMBRE_BBDD"></property> <property name="username" value="Chewbacca"></property> <property name="password" value="MUGRUUUUUUUHMA"></property> </bean>
Esta es una de las formas más usadas. El problema viene cuando empiezas a leer como funciona lo que estás usando. Os copio una imagen del javadoc.
Básicamente dice que esta clase no utiliza un pool de conexiones, y que si quieres usar un pool puedes usar alternativas, de hecho te proponen la que te voy a contar en este tutorial.
4. dbcp2 BasicDataSource de Apache
¿Y para que quiero yo un pool de conexiones en los tests?
Pues para lo mismo que en producción. Los tests, lo que hacen, cada uno de ellos que ejecuta una consulta en BBDD es lo siguiente. Abro conexión, hago cosas, cierro conexión. Y hacen eso por cada test, de forma secuencial. Es decir, que por cada test, estás abriendo y cerrando conexión. Esta operación es una operación muy costosa para la BBDD, y bases de datos tan populares y usadas como Oracle pueden tardar segundos (1-2 segundos) en crear una conexión nueva, con el consiguiente uso de CPU. Todo ello para ejecutar una query, que muy probablemente dure 0.001 segundos.
La solución es utilizar un pool de conexiones de tal forma que cuando un test termina, libera la conexión, que vuelve al pool, y el siguiente test la reusa, con lo que no gasta tiempo en abrir una conexión nueva. Mira lo fácil que es hacer el cambio.
applicationContext-test.xml
<bean name="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="url" value="jdbc:oracle:thin:@IP:PUERTO:NOMBRE_BBDD" /> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="username" value="OBIWAN" /> <property name="password" value="USETHEPOOLLUKE" /> <property name="removeAbandonedOnMaintenance" value="true" /> <property name="initialSize" value="1" /> <property name="maxTotal" value="15" /> </bean>
Además te recomiendo que como los tests se ejecutan en orden secuencial, de 1 en 1, no te vuelvas loco creando conexiones a la BBDD de inicio. Con 1, para la mayoría de casos es suficiente, y te ahorras esos segundos de reutilizar otras conexiones que nunca vas a usar. Por eso pongo initialSize a 1.
Para poder utilizar esa clase, solo tienes que añadir a tu pom.xml de maven la siguiente dependencia.
applicationContext-test.xml
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.1.1</version> <scope>test</scope> </dependency>
5. Conclusiones
Si haces TDD como nosotros en Autentia, vas a acabar con muchos tests. Algunos de esos serán de integración. En nuestro caso, en proyectos grandes podemos llegar a varios miles de tests de integración. Si por cada uno de ellos gastáramos 1 segundo en crear la conexión estaríamos horas esperando a que se terminaran, y no sería efectivo el pasarlos, ni la integración continua en Jenkins. Con este cambio conseguimos pasar esos miles de tests de integración en un par de minutos. Espero que te haya servido este tutorial, y que eleves el rendimiento de tus tests a la enésima potencia.