Test automáticos con QuickCheck ¿Cómo analizar nuestro código en busca de bugs?

Contrastar que nuestro código está libre de bugs es una labor mucho más difícil de lo que pudiese parecer en un comienzo. El número de relaciones que se generan entre las piezas de código que vamos agregando aumenta de forma mareante y por desgracia absolutamente nadie semeja saber todavía la manera de redactar código libre de bugs. No deja de ser sorprendente que el datamining y particularmente el machine learning no se apliquen de forma eficaz dada la ingente cantidad de programadores que hay en el planeta escribiendo cabezonamente exactamente el mismo código y cometiendo una y otra vez exactamente los mismos fallos.

Hasta ese momento, una de las herramientas que puede sernos útil en el momento de validar nuestro código es QuickCheck, que nos deja examinar automática y probabilísticamente nuestros algoritmos para localizar fallos.

¿Dónde se ocultan los bugs?

La experiencia nos enseña que un programador senior produce menos bugs que un programador junior, así como tenemos la impresión de que un programador senior va a escribir test de mejor calidad que un programador junior mas, ¿porqué?, ¿qué es lo que significa que un test es de mejor calidad que otro?

De manera intuitiva reconocemos relaciones que la experiencia semeja confirmar. Si escribimos un proceso que trabaja con cadenas y este es verificado para palabras como canario, baloncesto o bien picapiedra nos sorprendería que fallase para palabras como campanario, balonmano o bien piedrafita. Nos sorprendería menos hallar un fallo al contrastar con cadena vacía, símbolos o bien de longitudes diferentes. Si se trata de determinado cálculo contable (totales, descuentos, etc.) nos sorprendería que fuera ratificado para importes como diez y veinticinco, doce mil trescientos o bien siete.73 mas no para otros como diez y quince, veintitres mil doscientos o bien siete.75.

Y nos sorprendería por el hecho de que, aunque todos sabemos que acaban apareciendo bugs que fallan con balonmano mas no con baloncesto o bien con veintitres mil doscientos mas no con doce mil trescientos, no son los más usuales.

De este modo, siempre y en toda circunstancia de forma experimental y también intuitiva los bugs acostumbran a agruparse en clases (no excluyentes); o sea, existen clases de factores para los que nuestro código es o bien no adecuado. Como en los ejemplos precedentes, si comprobamos que nuestro código marcha para las cadenas , A, AA, AAA, AAAAA, AAAAAAA, … (número primo de aes) nos sorprendería que no funcionara para un número no primo de aes… ¡a menos que nuestro algoritmo esté relacionado de alguna manera con la primalidad!

Si supiésemos identificar, ¡para cada algoritmo!, las clases de factores que generan exactamente el mismo resultado en nuestro test solo deberíamos repasar un representante de cada clase y no todos.

Quickcheck

QuickCheck efectúa de manera automática una busca sobre el dominio de los factores procurando recorrer, exactamente, las más clases de factores que hemos indicado ya antes de manera que para cada test se realice una evaluación transversal.

Nosotros entonces, solamente proveemos una propiedad que debe cumplirse en la hipótesis de que nuestro código sea adecuado (un invariante) y Quickcheck lo examinará produciendo de manera automática los factores.

Por servirnos de un ejemplo, supongamos la próxima función en Java que señala si 2 cadenas son idénticas:

public static boolean equalStrings(String a, String b) undefined

Entonces utilizando una implementación de Quickcheck en Java podríamos contrastar si se cumple que cualquier cadena es igual a ella misma con el próximo test:

@Property
public void equalToItself(String xs) undefined

Quickcheck (en un caso así bajo JUnit) lanzaría cien, quinientos o bien los test que sean y conseguiríamos que todo es adecuado. Podríamos contrastar asimismo que una cadena a la que se agrega una X jamás es igual a esa cadena añadiéndole una Y con un test como:

public void differentSuffix(String xs) undefined

Y conseguiríamos asimismo que nuestra función marcha de forma perfecta.

Buscando invariantes

Si bien Quickcheck es una herramienta fabulosa para producir de manera automática razonamientos para nuestros test, no debemos olvidar que el dominio de busca aumenta exponencialmente con el tamaño de nuestros factores. Una función que toma como razonamiento tan solo 2 números de treinta y dos bits tiene un dominio de doscientos sesenta y cuatro = 18.446.744.073.709.551.616 elementos.

Por esta razón, la elección de los invariantes a contrastar prosigue siendo crítica, con lo que debemos buscar entre aquellos que conecten relaciones empleadas en la implementación mas de manera que cubran el dominio (no poner invariantes por poner). Pensemos en como hemos verificado que nuestra función equalStrings es adecuada, ¿cuantos casos de entre todos y cada uno de los posibles hemos descartado con los 2 test que hemos lanzado sobre la función equalStrings? ¡poquísimos! por el hecho de que de todos y cada uno de los pares de cadenas posibles, las cadenas iguales y las cadenas que solo disienten en la última situación (¡si bien consideremos las infinitas cadenas iguales y las infinitas cadenas que solo disienten en la última situación!) son solo 2 casos de entre otros posibles, en verdad, probemos con exactamente el mismo test precedente mas en vez de sufijos, prefijos:

@Property
public void differentPrefix(String xs) undefined

¡caramba! ahora el test falla y nos señala un inconveniente en nuestra implementación.

Conclusión

Los test básicos o bien los de regresión son triviales de incorporar por el hecho de que no pretenden validar la implementación, para hacerlo, deben producirse buenos test y esto dista mucho de ser simple. Quickcheck nos deja producir con sencillez miles o bien millones de casos de prueba mas prosigue siendo crítico saber identificar el género de código que estamos procurando validar para buscar y acotar aquellos invariantes que son más transversales a las clases de factores para los que es pero sensible nuestro código.

No ha sido mi pretensión redactar un tutorial sobre Quickcheck, sino más bien llamar la atención sobre tal herramienta y resaltar la relevancia de entender cuando, de qué forma, porqué y exactamente en qué medida marcha la busca de bugs a través de test unitarios. Si deseas darle una ocasión, probablemente haya una implementación para tu lenguaje preferido.

hljs.initHighlightingOnLoad();

code.hljs undefined
@media only screen and (min-width: 768px) undefined
@media only screen and (min-width: 1024px) undefined

Asimismo te invitamos a

BDD, Cucumber y Gherkin. Desarrollo dirigido por comportamiento

Testando tus aplicaciones Java con Spock: tests más expresivos, simples de leer y sostener

¿Nos están fastidiando los móviles las manos?

– La nueva

Test automáticos con QuickCheck ¿De qué manera examinar nuestro código en pos de bugs?

fue publicada originalmente en

turincon.net

por
Jose Juan

.