TDD: con qué tipo de aplicaciones se debe usar testing unitario


(Ana Pardo) #1

Buenas Tardes,

En mi equipo de trabajo estamos empezando a adentrarnos en los temas de Extreme Programing. Nos hemos encontrado con una pared tratando de implementar TDD. Quisiera saber su opinión, experiencia, consejos, etc…

Estamos trabajando con PHP, framework symfony 3 y base de datos mySql.

A continuación detallo la situación:

Actualmente estamos desarrollando una aplicación que esta basada en transacciones de formulario. Toda la aplicación interactúa a través de formularios, guardar información, mostrar información, edición, etc. No tenemos actualmente ninguna funcionalidad que procese o altere de ninguna manera datos. Las validaciones de los formularios se realizan a través de jquery y todo el site se maneja con ajax.

Estamos incluyendo el TDD en las funcionalidades nuevas que estamos desarrollando, pero nos hemos encontrado con dificultades para testear funcionalidades como guardar formulario (por ejemplo, Guardar Nuevo Usuario).

En los artículos y foros que hemos leído, nos encontramos con que testing unitarios no deberían implementarse en este tipo de funcionalidades, que debería utilizarse test funcionales. Por esto, después de batallar un rato con las pruebas unitarias, decidimos pasar a hacer pruebas funcionales. Sin embargo, nos volvimos a topar con problemas por el uso de ajax.

La duda concreta es: Cuando se tienen aplicaciones de este tipo, donde no hay un procesamiento de datos, sino transacciones a base de datos, con uso de ajax y validaciones con jquery. Vale la pena el esfuerzo de implementar estos test en código? O sería mejor usar software externo como Selenium para hacer los test automatizados del sistema?

En caso de que si sea importante tener testing unitario o funcional, hay alguna herramienta/plugin/aplicación que haga el proceso un poco menos doloroso?

De antemano muchas gracias por la ayuda :slight_smile:


(Aristides Castillo) #2

Hola!

En principio, quiero felicitarles por mejorar sus prácticas ágiles con la inclusión de TDD, ya que esto es lo que realmente te pone en el carril de alta velocidad en el desarrollo ágil.

Es por lo anterior que en realidad no se habla de que haya un tipo de aplicacion que no merezca o requiera incluir pruebas, y puedo señalar varios ejemplos de aplicaciones que hacen algo como un CRUD y aun asi incluyen pruebas unitarias, de integración y funcionales. Si solo se incluyen pruebas funcionales end-to-end, de las del tipo que puedes hacer con Selenium, estás incluyendo prácticas de QA, pero no estás haciendolo de manera ágil.

El objetivo de que las pruebas unitarias o de integración permanezcan en código es para que los desarrolladores puedan ejecutarlas varias veces durante su proceso de desarrollo, con una frecuencia similar a aquella con la que compilan o hacen commit al repositorio. Si esas pruebas no pueden ejecutarse asi de facil, entonces se pierde el efecto de red de seguridad que una suite de pruebas unitarias te da para saber si un cambio antes de hacer commit rompe o no la estabilidad del código. Por esa misma razón, debe existir pruebas unitarias de una funcionalidad tal como Guardar Nuevo Usuario, para asi saber si un cambio posterior en el código que pueda chocar con esa funcionalidad aun permite o no que ella siga funcionando.

Lo que mencionas del problema de AJAX / jQuery creo que más bien apunta a otro tema, y es que, para bien o para mal, se necesita que la arquitectura de la aplicación contemple ciertas reglas para permitir que sea testeable, tanto a nivel de integración como a nivel unitario. Es perfectamente posible que un Web Endpoint, diseñado para su uso como API o a través de un AJAX request, sea testeable de manera unitaria. Lo importante es que la arquitectura esté modularmente diseñada para que los componentes puedan operar de manera unitaria y asi ser probados por separado. Es por esta razón que es muy facil diseñar una aplicación que no puede ser unitariamente probada.

Frameworks como Symfony3, CakePHP, Laravel, Yii, por nombrar algunos frameworks PHP, todos incluyen algun mecanismo para probar unitariamente sus CRUDs. Por lo tanto, yo no apuntaria ni a la tecnologia usada, ni al tipo de aplicación para detectar por que se les hace tan dificil probar unitariamente, sino que lo evaluaria en el diseño arquitectónico.

Saludos,
Arístides Castillo


(Agustin Villena) #3

Hola Ana

TDD al principio parece magia negra. Como que no se puede creer que sea incluso posible el definir test antes del codigo.

Una lucha histórica es entre lo que creen que es posible y tiene valor el automatizar pruebas en las Interfaces de Usuario, y los que piensan que es una pérdida de tiempo.

Sin embargo si he visto consenso en la necesidad de probar el modelo, y en particular la fracción del modelo que usa una vista en particular (que se llama “View Model”)

Te recomiendo echarle una mirada al modelo de arquitectura usado MVVM https://en.wikipedia.org/wiki/Model–view–viewmodel y como probarlo, aqui un articulo sobre TDD sobre MVVM https://dukescript.com/best/practices/2015/02/16/tdd-with-dukescript.html

Saludos
Agustin


(Ana Pardo) #4

Agustin Muchas Gracias!! Le voy a dar una mirada :slight_smile:


(Ana Pardo) #5

Aristides, Hola :slight_smile:

La arquitectura de la aplicación es MVC. Tenemos una capa donde están todas las vistas, una capa controlador y una capa modelo donde se encuentran las entidades y los repositorios de las entidades.

En el controlador se obtienen los valores del formulario, se ingresan dentro de un objeto o se envían directamente a los repositorios, en donde se hacen algunas validaciones, en los casos que corresponden, se estructuran los objetos que serán guardados y se persiste a la cada de datos.

Las validaciones que se realizan en código PHP son pocas, ya que la mayoría se hace desde la vista Modelo con jQuery.

Me hace bastante sentido que en la arquitectura pueda estar nuestra dificultad, ya que no hay una capa de negocio que se encargue de validar o de manejar las estructuras que entran o salen de base datos. Y es por ello que quizás nosotros dudamos un poco de la utilidad de las pruebas unitarias ya que para todas las pruebas de los métodos en los repositorios debemos usar MOCKS, y sentimos que toda la funcionalidad se simula.

Si en realidad es la arquitectura nuestro problema, que sería lo más recomendable? Cambiar la arquitectura?

Gracias por tu ayuda, Saludos


(felipetv) #6

@apardo

tengo unas dudas:

que framework estan usando para hacer testing?
y si ya han hehco alguna experiencia con testing fuera del proyecto actual?


(RIcardo M.) #7

Si usan MVC deberían tener la logica de negocio, validaciones, etc. en los modelos y esos si se pueden probar unitariamente.

Tener solo validaciones en la vista y con JQuery es mala idea especialmente por un asunto de seguridad… basta con desactivar Javascript, o incluso ver el codigo HTML para saber la URL usada para guardar/actualizar los datos y luego empezar a jugar a ser hacker…


(Ana Pardo) #8

@felipet , Estamos usando phpUnit y ninguno en el equipo tiene experiencia con Testing Unitario.


(felipetv) #9

yo partiria

por hacerles una kata de TDD en PHP, como el ultimo coding dojo que hicimos. eso ayudaria a adaptarse a la metodologia

tambien le exaria un vistazo a selenium para los UI test.
https://phpunit.de/manual/3.7/en/selenium.html

saludos!.


(RIcardo M.) #10

Y porque no usan el que trae Symfony? suponiendo que Symfony trae algo para hacer tests unitarios, segun recuerdo es similar a CakePHP que hace años trae tests… hasta se pueden crear automagicamente al igual que los CRUD de los modelos/controladores/vistas.


(Aristides Castillo) #11

Pues lo que comenta Ricardo es un punto muy válido. Toda la lógica solamente en JS es mala idea. Incluso en el escenario de una SPA (React, Angular) donde vas a tener una buena porción de lógica en JS, igual necesitas tener logica de validación a nivel del API en el backend, y eso mismo deberia estar pasando con tus controladores y el modelo en Symfony/PHP. De lo contrario, es un hueco de seguridad tremendo.

Yo no sugeriria hacer un cambio de arquitectura solo para hacerla testable, ese es el tipo de cosas que deben incluirse desde un primer momento y ya cuando estás en un estadío avanzado, pues lo que puedes hacer es alguno que otro refactoring con esa intención… pero si tu arquitectura actual está dejando la aplicación desprotegida a posibles ataques debido a que todo solo está a nivel de JS (que bien puede apagarse en cada browser), esa si es una buena razón para hacer una revisión profunda de la arquitectura.

También existen excelentes frameworks de unit testing para JS, y si tienes una buena porción de lógica allí, yo consideraría introducir pruebas unitarias a ese nivel.


(Germán G.) #12

Hay una persona que se llama Nicolás Paez que podría darte una asesoría @apardo

Él va a venir a Chile para el evento SoCraTes el 1 de Julio, sería una buena oportunidad para pedirle que les ayude directamente onsite en su lugar de trabajo. ¿Qué te parece?


(Ana Pardo) #13

@pobrezuko estamos utilizando phpUnit porque es lo recomendando por la bibliografía de Symfony.
Antes de empezar a utilizar testing unitario evaluamos otras herramientas, pero finalmente decidimos usar phpUnit dandole más peso a que era el que estaba dentro de la bibliografía de Symfony.


(David Lay) #14

En el mundo web, se suele separar harto lo que es front-end (la V de MVC) y backend (M y C de MVC).
En los desarrollos web que he desarrollado, he llegado a entender que estas dos partes deben ser lo más independientes posibles, ya que requieren de consideraciones muy diferentes. En front-end, hay un paso mucho más acelerado y experimental en cuanto a lograr una experiencia de usuario óptima y adaptarse a los distintos dispositivos que acceden al sitio, mientras que en el back-end, nos esforzamos por hacer desarrollos sólidos que soporten los requerimientos de datos del front-end, que sean eficientes, seguros y mantenibles.

Por esto suguiero tratarlos de manera distinta. El front-end también requiere de desarrolladores, en javascript, y el código, como cualquier otro, debe ser probado. En general, no recomiendo el uso de jquery ya que hace muy complicada esta tarea, debido a que tiene un enfoque más de scripting que de aplicación. Frameworks y librerías más modernas como Angular, Ember, React, etc, permiten una correcta separación de componentes que se pueden probar fácilmente. Si necesitas mantenerte con jQuery, puedes usar https://qunitjs.com/ que es un framework de unit testing que ellos mismos desarrollan.

El desarrollo de la aplicación front-end puede llegar a ser tan complicado y demandante como el del backend, llegando a tener ambos un patrón MVC en donde uno habla con el otro, algo como M(MVC)C

El backend, aparte, debe contar con todas las validaciones y reglas de negocio que siempre ha tenido que tener históricamente, ya sea exponiendo una API JSON hacia una aplicación full ajax, o haciendo render de html+css+js para cada request. En el backend de un sitio, de hecho, puedes tener varios servicios trabajando en conjunto y mantenidos por distintos equipos.

Hacer esa separación temprano te puede ayudar a liberar a tu equipo de varias frustraciones, permitiendo que se enfoquen en componentes y consideraciones distintas dependiendo de qué es lo que estén desarrollando (front o back)


(Rafael Avaria Gutierrez) #15

Le puedo dar mi visión de TDD y BDD. TDD es algo difícil de implementar a mi gusto se supone que vas a testear métodos que aún no has creado, es difícil definir responsabilidad sobre algo que estas construyendo.
A menudo una clase tiene varios métodos, no tienes claro cuales, pero si sabes concertesa el comportamiento que debe tener.

BDD se centra en el comportamiento, si esta clase recibe este parámetro de entrada su salida es esta. Si para lograr ello debe usar tres métodos uno publico y dos privados, no es problema a fin de cuentas nadie sólo la misma clase tiene acceso a ellos. es más fácil centrarse en comportamiento de la entidad que en cada uno de los métodos necesita para cumplir con lo se supone que se le pide, al menos cuando esta creando una nueva funcionalidad.

Por otra parte, si la responsabilidad esta clara, por ejemplo un clase helper para manejo de fechas, TDD viene como anillo al dedo, cada método tiene una respuesta definida y en este caso interesa testear cada método de la entidad.


(Guillermo Schwarz) #16

Hola,

Que burno saber que alguien.se.esta metiendo en.el.maravilloso mundo de los.test unitarios y va a dejar los defectos en.el.pasado.

Concuerdo de que es buena idea crear pruebas unitarias con phpunit, pero para que funcione debes crear clases y probarlas. Eso es fundamental y la mayor parte de la gente no lo hace y no tiene como aplicar pruebas.unitarias.

Toma tiempo aprender a usar programacion orientada al objeto, en comparacion hacer pruebas unitarias es trivial.

Como dato anecdotico te puedo decir que si tienes un programa orientado a objetos y no tienes escritas las pruebas unitarias, ese programa no es orientado a objetos.

Aun mas, si no ocupas patrones de.diseño y SOLID del uncle bob, tampoco es orientado a objetos. Puede sonar contraproducente, pero la orientacion a objetos no es un objetivo en si, lo importante es lograr ciertas caracteristicas que estan descritas en SOLID.

Ahora bien, despues que todo.eso esta dicho
Ocurre que tdd y bdd son conceptualmente lo mismo, solo son distintas notaciones, uno le gusta una a otro otra… y las pruebas funcionales es sobre lo.que el usuario ve, son pruebas mucho mas grandes.

Lo que pasa con las pruebas funcionales es que corren dentro de la integracion.continua o corren en la noche, con jenkins, y te enteras rapidamente.de.lo.que.se rompio. La.regla genersl es.que quien rompe arregla y mientras no.este arreglado, nadie hace push excepto quien debe arreglar el repo.

Eso.esta muy bien, pero cuando solo hay pruebas funcionales, alguien debe debuggear los.tests para descubrir el problema causal, mientras que con las prurbas unitarias nunca mas.hay que abrir el.debugger.

La ventaja es obvia: si se cae un test unitario, es obvio lo.que se cayó. O por lo menos el nombre deberia indicar cual es el problema, si no lo hacemos asi… no entendemos lo que es TDD.

Ahora bien, dada la descripcion de tu proyecto… si no tiene modificacion de los datos que estan en la.base de datos, para que sirven los.formularios.

Tengo la tendencia de programar programas generales que solicionan un problema de una vez para siempre, y lo que tu dices parece un candidato genial para hacer justamente eso.

Saludos,
Guillermo.


(Ana Pardo) #17

@Guillermo_Schwarz, @Raff_cl, @davidlaym Muchas Gracias por sus comentarios.

Luego de leer sus experiencias, creo que vamos a tener que rediseñar la forma en que estamos validando los formularios para separarlos más de la lógica de guardado.

@Guillermo_Schwarz, la data de base de datos si se modifica, lo que no se modifica es la data que viene desde el formulario. Es decir, se toma la información y se hacen algunas validaciones como que algún campo no esté vacío y luego se guarda tal cual.

Quizás haciendo estas validaciones en métodos separados podríamos hacer testing unitario sobre estos, y además incluir BDD.

Muchas Gracias a todos.


(Guillermo Schwarz) #18

Hola Ana,

Como yo le veo es así: lo que estás implementando es un CRUD (Create - Read - Update - Delete), puedes crear un “mantenedor” [ nomenclatura de los 80’s] que permita hacer las 4 operaciones sobre las tablas.

Hay muchas maneras de hacer esto. La primera vez que lo hice leía una configuración de un archivo ASCII que indicaba los campos, los tipos, las validaciones,etc.

Después me di cuenta que casi toda la información está en la BD, de modo que leía la metadata de la BD… (se puede hacer en Java usando JDBC, de modo que al partir el programa lee la cantidad de tablas, la cantidad de campos, la FK entre tablas incluyendo la información de los campos relacionados, etc.), Eso puede demorar un poco, de modo que se escribe la estructura de la BD usando Dozer en un archivo XML que se carga en un par de segundos.

¿Qué ventaja tiene esto? Qué sé que valores puede tomar cada campo y aparecen combo boxes con los valores permitidos. Además si en la BD SQL aparece NOT NULL, significa que no puedes dejarlo en blanco, por ejemplo. Otras ventajas es que algunas validaciones complejas puedes colocarlas en archivos de texto, que son luego interpretadas por tu programa.

¿Porqué uno haría algo tan complicado?

Porque programar algo general es tan caro como programar algo específico. Si tienes 300 tablas que actualizar, puedes navegar el modelo y actualizarlo mientras el programa está ejecutando. Eso te da mucha flexibilidad y el programa no se desactualiza cuando cambias las reglas o cambias el modelo de BD, todo es por configuración.

¿Donde se aplican los tests unitarios?

En el CRUD mismo. Debes tener un modelo de BD pequeño donde se hagan las pruebas unitarias, lo que permite veirifcar que tu CRUD maneja todos los casos de borde.

¿Dónde se aplican los tests funcionales?

Los tests funcionales se aplican sobre tu aplicación completa, incluyendo el modelo real de BD. Ya probaste el CRUD y ahora lo que tienes que hacer es verificar que tu configuración sobre el modelo de BD real efectivamente cumple las funcionalidades pedidas.

Una de las críticas que la gente que no sabe OOP hace sobre hacer mantenedores es que no hay manera de poner lógica complicada sobre el mantenedor. Eso es cierto si estás usando programación estructurada, pero en OOP tú puedes darle comportamiento específico a ciertas clases, en este caso, dar un comportamiento específico a una tabla de BD simplemente creando una subclase con ese nombre y ese comportamiento, desde que se vea diferente a que haga validaciones especiales o algo completamente diferente.

BDD es impresionante, porque es muy fácil de leer, quizás Felipe te puede contar más de cómo lo hace él.

Saludos,
Guillermo.


(felipetv) #19

echenle un viztazo a cucumber


(Hans Poo) #20

Ana,

La verdad es que uno de los problemas que veo está en el concepto de “Mantenedor” que mencionas.

Te cuento una historia: Hace muchos años un Conocido me pidió desarrollar un sistema, que para el era muy simple, y lo definió asi: “Hay que meter cosas a la base de datos, y mostrar cosas de la base de datos”, cuando comprendí lo que decía me dió tentación de risa, y le dije: “Pero si esto es lo que hacen todos los sistemas”… plop.

Robert Martin por el contrario, afirma que los clientes le pagan por comportamiento y no por datos.

Con esto quiero decir, que debes buscar el comportamiento en tu sistema, y crear pruebas unitarias para probar “ese comportamiento”… y por supuesto ir quitando la tentación de reducir los sistemas a mantenedores.

Atte,
Hans