Prueba unitaria: aplicación acoplada a la base de datos

15

¿Cuál sería el mejor enfoque en la unidad de prueba de un modelo que se integra en una aplicación que está estrechamente vinculada a la base de datos?

El escenario específico aquí es un carrito de compras: me gustaría poder probar la adición, la eliminación y la recuperación de artículos del carrito, así como la lógica de precios, etc. Esto en mi mente requiere acceso a la base de datos, aunque he leído varias veces que Se debe evitar el acceso a la base de datos.

usuario1189880
fuente
1
Interesante que las respuestas que efectivamente dicen "reescribe el código de tu aplicación" se voten
AD7six

Respuestas:

10

La inyección de dependencia es una forma de manejar esto. Puede configurar una base de datos de prueba para imitar el carrito de compras, o incluso puede escribir algún código que "confirme" la transacción del cliente. Luego, en tiempo de ejecución, su software elegirá a qué componente conectarse.

¡Simplemente no se conecte a la base de datos de producción para nada durante las pruebas!

chrisaycock
fuente
1
Con DI y el diseño adecuado de la aplicación, debería poder realizar pruebas sin ninguna base de datos, siempre que la simulación que inyecte proporcione suficiente burla detallada de la base de datos de fondo.
Peter K.
4

En la prueba unitaria, debe definir el límite de lo que está probando. Las pruebas unitarias son diferentes de las pruebas de integración. Si la lógica de fijación de precios es independiente del contenido del carrito, lo prueba por separado. Si este no es el caso, y todos los módulos están estrechamente acoplados, cree un entorno de prueba que imite la producción tanto como pueda y trabaje con eso. No creo que los atajos y la simulación ayuden a largo plazo.

Ninguna posibilidad
fuente
2

El modelo no debe depender de un DB (concreto). Si solo conoce un DB abstracto (lea "interfaz") que se entrega al modelo, entonces puede reemplazar el DB con un objeto simulado .

En la programación orientada a objetos , los objetos simulados son objetos simulados que imitan el comportamiento de los objetos reales de manera controlada. Un programador generalmente crea un objeto simulado para probar el comportamiento de algún otro objeto, de la misma manera que un diseñador de automóviles usa un maniquí de prueba de choque para simular el comportamiento dinámico de un humano en los impactos de vehículos ...

EricSchaefer
fuente
1

Tuve un problema similar: no tenía posibilidad de garantizar que mi DB de prueba mantenga los valores. Así que en el futuro obtengo, por ejemplo, otros precios.

Extraje los datos que necesitaba en un pequeño sqlite -DB y usé este DB para mis pruebas. El Test-DB ahora es parte de la configuración de mi prueba unitaria.

Knut
fuente
2
El objetivo de las pruebas unitarias es probar su código de forma aislada. Si usa una base de datos sqllite, entonces no está aislada. También inconsistencias entre las bases de datos pueden causar errores
Tom Squires
0

"Lo mejor" es subjetivo, pero podría usar una conexión de prueba de base de datos.

Use dispositivos para cargar algunos datos de prueba (productos de ejemplo para comprar) y luego escriba el caso de prueba para la clase / función que desea probar.

AD7six
fuente
Describir las pruebas unitarias que prueban una función que actúa en una base de datos como pruebas de integración es bastante engañoso @murph.
AD7six
1
Ok, ahora estoy profundamente confundido: si se trata de una base de datos, no es, según la mayoría de las definiciones, una prueba unitaria porque no es autónoma. Si tiene una base de datos, entonces está ejecutando pruebas en un nivel superior, uno que tenía dependencias, uno que busca "combinar" cosas. Independientemente de esto, no es una explicación clara para mi mente de cómo resolver el problema.
Murph
0

Creé un complemento para Symfony 1.4 (PHP) para abordar este problema (entre otros). Está modelado según la forma en que opera el framework de prueba de Django (Python) : el framework construye y llena una base de datos de prueba separada antes de que comience cada prueba, y destruye la base de datos de prueba una vez que se completa cada prueba.

Tenía un par de preocupaciones sobre esta estrategia, tanto en términos de rendimiento (si el esquema no cambia, ¿por qué no simplemente borrar los datos en lugar de reconstruir la estructura completa?) Y conveniencia (a veces quiero inspeccionar la base de datos después de un prueba fallida, ¡así que no la destruyas indiscriminadamente!), así que tomé un enfoque ligeramente diferente.

Antes de que se ejecute la primera prueba, la base de datos se destruye y se reconstruye, en caso de que haya habido cambios en el modelo desde la última prueba. Antes de que se ejecute cada prueba posterior, los datos en la base de datos se borran, pero la estructura no se reconstruye (aunque una reconstrucción manual se puede activar a partir de una prueba si es necesario).

Al cargar de forma selectiva los dispositivos de datos en cada prueba, se puede crear el entorno adecuado para esa prueba sin interferir con las pruebas posteriores. Los archivos fixture también se pueden reutilizar, lo que hace que esta tarea sea mucho menos onerosa (¡aunque sigue siendo mi parte menos favorita de las pruebas de escritura!).

En ambos marcos de prueba, el adaptador de base de datos está configurado para usar la conexión de prueba en lugar de la conexión de "producción" para evitar que la ejecución de la prueba corrompa los datos existentes.


fuente
0

Yo diría que solo siga adelante y use accesorios para precargar los datos. Así es como los marcos de pruebas unitarias parecen funcionar en general, cuando se prueba la manipulación de datos.

Pero si realmente desea evitar tener que conectarse a una base de datos de cualquier tipo y seguir la definición demasiado estricta de que las pruebas unitarias no tocan nada fuera del código, eche un vistazo a la burla de objetos: puede darle ideas.

Por ejemplo, en lugar de soltar el SQL directamente en el código donde lo necesita, tenga una forma de llamar a un método que solo haga lo que hace ese SQL. Use Person.getPhoneNumber(), por ejemplo, en lugar de SELECT phone_number FROM person WHERE id = <foo>. No solo es más limpio y fácil de entender de un vistazo, sino que durante la prueba puede burlarse del objeto Persona para que getPhoneNumber()siempre regrese 555-555-5555o algo así, en lugar de tocar la base de datos.

Izkata
fuente
0

Esto es bastante fácil de hacer con junit si está un poco largo sin aliento.

La "configuración" debe definir y completar un conjunto de tablas temporales.

Luego puede realizar las pruebas unitarias para todas las funciones de actualización, inserción y eliminación.

Para cada prueba, llame a su método de actualización y luego ejecute algunos SQL para verificar el resultado esperado.

En la fase de "desmontaje", sueltas todas las tablas.

De esta manera, siempre ejecuta las mismas pruebas en los mismos datos iniciales. Si mantiene las tablas entre las pruebas, terminan siendo "contaminadas" por pruebas fallidas, también, una prueba consistente de "inserción" es casi imposible ya que necesita seguir inventando nuevas claves en cada prueba.

James Anderson
fuente