¿Cómo puedo usar pruebas unitarias y TDD para probar una aplicación que se basa principalmente en operaciones CRUD de bases de datos?

22

En el trabajo, uno de mis proyectos consiste principalmente en tomar datos pasados ​​de un cliente externo y conservarlos en una base de datos. Es una aplicación empresarial de Java que utiliza JPA y la mayor parte de nuestra lógica gira en torno a las operaciones CRUD.

La mayoría de nuestros errores involucran JPA de una forma u otra.

  • Ejemplo 1: si hace clic dos veces en el botón Guardar, JPA podría intentar insertar la misma entidad en la base de datos por segunda vez, causando una violación de clave principal.
  • Ejemplo 2: recupera una entidad de la base de datos, la edita e intenta actualizar sus datos. JPA puede intentar crear una nueva instancia en lugar de actualizar la anterior.

A menudo, la solución necesita agregar / eliminar / cambiar una anotación JPA. Otras veces tiene que ver con modificar la lógica DAO.

No puedo entender cómo obtener confianza en nuestro código utilizando pruebas unitarias y TDD. No estoy seguro de si es porque las pruebas unitarias y el TDD no encajan bien, o si me estoy acercando al problema mal.

Las pruebas unitarias parecen encajar mal porque solo puedo descubrir estos problemas en tiempo de ejecución y necesito implementarlos en un servidor de aplicaciones para reproducirlos. Por lo general, la base de datos debe estar involucrada, lo que considero que está fuera de la definición de una prueba unitaria: estas son pruebas de integración.

TDD parece un mal ajuste porque el bucle de retroalimentación de implementación + prueba es tan lento que me hace muy improductivo. El bucle de retroalimentación de implementación + prueba lleva más de 3 minutos, y eso es solo si ejecuto las pruebas específicamente sobre el código que estoy escribiendo. Ejecutar todas las pruebas de integración lleva más de 30 minutos.

Hay un código fuera de este molde y siempre hago pruebas unitarias siempre que puedo. Pero la mayoría de nuestros errores y los sumideros de tiempo más grandes siempre involucran JPA o la base de datos.


Hay otra pregunta que es similar , pero si siguiera el consejo estaría envolviendo la parte más inestable de mi código (el JPA) y probando todo menos eso. En el contexto de mi pregunta, estaría en la misma mala situación. ¿Cuál es el siguiente paso después de envolver el JPA? En mi opinión, esa pregunta es (quizás) un paso para responder a mi pregunta, pero no una respuesta.

Daniel Kaplan
fuente
44
Lo que está haciendo es esencialmente una prueba de integración, ya que debe configurar la base de datos para probar realmente. Me imagino que un módulo dependería de otros, así que hágalo aún más como una prueba de integración. Cambiaría la pregunta que tiene sobre cómo aplicar los enfoques TDD a su aplicación.
InformadoA
@randomA correcto, edité mi pregunta para decirlo explícitamente. No entiendo por qué me recomiendan cambiar la pregunta. ¿Puedes elaborar? Quiero mantener la parte de prueba de unidad allí porque había más bien estar escribiendo pruebas unitarias que las pruebas de integración (aunque soy consciente de que unit testing != TDD)
Daniel Kaplan
nada especial, solo pon TDD allí. Si tiene una prueba unitaria allí, entonces muchas personas pensarían que no entiende algo, etc., no es bueno para usted ...
InformadoA

Respuestas:

7

Una opción es usar una base de datos de prueba en memoria como H2 ; tiende a ser aproximadamente 10 veces más rápido que una base de datos estándar que usa discos, y con tiempos de arranque / desmontaje más bajos.

Si ayudará, dependerá en gran medida de si los problemas de JPA que está teniendo son lo suficientemente generales como para que aún fallen en diferentes bases de datos. No tiene mucho sentido ejecutar pruebas más rápido si pierden la mayor parte de los problemas.

Pero si puede hacer 10 carreras con H2 por cada una con el sistema completo, podría dar sus frutos.

soru
fuente
Es un buen pensamiento, pero todavía tendría que implementarlo en el servidor de aplicaciones, AFAIK. Eso es mucho de los más de 3 minutos. Dicho esto, definitivamente vale la pena hacerlo. Pero aún es difícil imaginar ejecutar las pruebas con tanta frecuencia como yo haría pruebas unitarias y, por lo tanto, parece ineficiente desarrollarlo usando TDD.
Daniel Kaplan
1
Creo que generalmente hay formas de evitar ese requisito (por ejemplo, docs.oracle.com/middleware/1212/toplink/TLADG/testingjpa.htm ). Sin embargo, hay una probabilidad bastante alta de ser más trabajo que justificado; Otra opción sería obtener algunos servidores de prueba más robustos y ejecutar cosas en paralelo.
soru
1
@tieTYT Mi propia prueba de concepto con la unidad hsqldb probando una aplicación web de mala calidad en github: TestingWithHsqldb : las pruebas unitarias no necesitan que la aplicación se implemente.
3

Las bases de datos pueden ser muy fáciles de probar: necesita procedimientos y transacciones almacenados.

Esto es lo que Microsoft dice acerca de las pruebas de unidad de base de datos . También puede ejecutar pruebas unitarias en una base de datos, escribir sus pruebas en Java o C # mediante la configuración de una conexión de base de datos, comenzar una transacción, escribir los datos que desea utilizar para la prueba en la base de datos, ejecutar las pruebas y luego deshacer. No hay daños en la base de datos si estaba usando uno en el que también se implementó y obtiene pruebas completamente aisladas.

Espero que esto pueda darle una idea de cómo hacerlo dentro de su marco.

gbjbaanb
fuente
Como dije, "La mayoría de nuestros errores involucran a JPA de una forma u otra", creo que el consejo del artículo los echaría de menos. Además, si considera que esas pruebas Java / C # siguen siendo pruebas unitarias, tenemos definiciones muy diferentes. Creo que este es un buen consejo en general, pero todavía parece que tomaría mucho tiempo implementar y ejecutar la suite y, por lo tanto, no es propicio para TDD. ¿Estás en desacuerdo?
Daniel Kaplan
Solíamos ejecutar pruebas unitarias de base de datos para nuestro SQL, pero luego estaban todas en sprocs. Si bien puede probar el directorio sql desde otros procedimientos sql, nuestro marco de prueba unitario fue MSTest, por lo que tenía sentido ejecutarlos desde allí (hey, obtuvimos marcas verdes en el servidor de compilación, que fue el factor más importante). Si tiene una base de datos siempre activa (y lo hicimos para las pruebas internacionales de todos modos), es fácil cargar todo el código sql y ejecutar todas las pruebas unitarias en el servidor de compilación. A veces solo tienes que ser pragmático sobre estas cosas.
gbjbaanb 01 de
No creo que hayas respondido a mi primera oración.
Daniel Kaplan
bueno, solo usa jpa-unit entonces. No puedo responder cómo funciona su código JPA (o no), solo trate de darle algunas ideas sobre cómo probar ese sql en la base de datos.
gbjbaanb 01 de
3

Otras personas han respondido con "¡Burla tu DB!" - ¿Pero cuál es el punto de burlarse de su capa de base de datos si realmente necesita probar cómo interactúa con su código?

Lo que está buscando son pruebas de integración y / o pruebas de IU automatizadas. Usted mencionó que el problema ocurre cuando:

*If you click the save button twice*

La única forma de probar esto es escribir una prueba de IU automatizada para hacer clic dos veces en el botón. Tal vez echa un vistazo a Selenium.

Probablemente también necesitará una unidad de prueba DB y para sus pruebas apúntelo hacia eso. Un dolor de mantenimiento pero bienvenido a TDD en el mundo real.

Rocklan
fuente
esto se lee más como una diatriba que una respuesta
mosquito
He respondido la pregunta tres veces: pruebas de integración, pruebas de GUI y / o una unidad de prueba DB. Sí, es un poco despotricar, lo editaré con cierta apariencia de cordura ahora.
Rocklan 01 de
1
"La única forma de probar esto es escribir una prueba de IU automatizada para hacer clic en el botón dos veces. Tal vez eche un vistazo a Selenium". En situaciones como esa, es mejor que el backend evite que eso ocurra, de lo contrario, la interfaz de usuario tendría acceso directo a la base de datos.
Daniel Kaplan
0

En el ejemplo que da en su pregunta, no puede hacer una prueba de unidad / TDD en la situación de hacer clic en el botón dos veces para causar un error muy fácilmente. Pero lo que puede hacer una prueba unitaria es que en el código que se llama cuando hace clic en el botón, si obtiene una excepción de la capa de persistencia, la maneja adecuadamente (burlándose de la capa de persistencia o usando una base de datos en memoria como ha sido sugerido en otras respuestas), ya sea volviendo a lanzar o mostrando un error o lo que sea.

Tiene razón en que TDD puede comenzar a descomponerse cuando necesita realizar pruebas que no se ajustan bien a una prueba unitaria (es decir, pruebas de integración / sistema); esto formó bastante debate en el reciente "Is TDD ¿Muerto?" debates entre Kent Beck, Martin Fowler y David Heinemeier Hansson: http://martinfowler.com/articles/is-tdd-dead/

Chris Cooper
fuente