¿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.
unit-testing
usuario1189880
fuente
fuente
Respuestas:
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!
fuente
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.
fuente
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 .
fuente
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.
fuente
"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.
fuente
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
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 deSELECT 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 quegetPhoneNumber()
siempre regrese555-555-5555
o algo así, en lugar de tocar la base de datos.fuente
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.
fuente