scieee AI-readable full text Open interactive document viewer

Generación automática de oráculos de prueba para APIs REST

Alonso Valenzuela, Juan Carlos

Abstract

La generación automática de casos de prueba para APIs RESTful es un área de investigación en auge, dado el rol que desempeñan en la integración software. La mayoría de las propuestas en este ámbito siguen un enfoque de caja negra, en el que los casos de prueba se generan automáticamente a partir de la especificación de la API. A pesar de los prometedores resultados de estas propuestas, todas se encuentran limitadas por los tipos de errores que pueden detectar: fallos de servidor no controlados (etiquetados con un código 500) y disconformidades con la especificación de la API. La falta de técnicas para la detección de errores específicos del dominio de cada API supone una importante limitación para el grado de automatización obtenido por estas herramientas. En este trabajo, se propone un enfoque basado en la detección de invariantes (i.e., propiedades que siempre se cumplen en uno o más puntos de la ejecución de un programa) para automatizar el proceso de generación de oráculos de prueba, que pueden ser utilizados para la creación de assertions. En concreto, la propuesta recibe como entrada la especificación de la API y un conjunto de pruebas generadas automáticamente (para las que se conocen únicamente los valores de las entradas y la salida devuelta) y devuelve un conjunto de invariantes que pueden ser utilizados como oráculos. Los resultados obtenidos en una evaluación realizada sobre un conjunto de 8 operaciones de 6 APIs comerciales muestran la capacidad de la propuesta para generar cientos de oráculos válidos, llegando a obtener una precisión del 100 % (y una precisión total del 66.5 %) y detectando un total de 6 bugs replicables en 4 operaciones pertenecientes a 3 sistemas con millones de usuarios (Amadeus Hotel, GitHub y OMDb).

Full text

TRABAJO FIN DE M´ ASTER Generaci´on Autom´atica de Or´aculos de Prueba para APIs REST Realizado por Juan Carlos Alonso Valenzuela Para la obtenci´on del t´ıtulo de M´aster en Ingenier´ıa del Software: Cloud, Datos y Gesti´on TI Dirigido por Sergio Segura Rueda Convocatoria de Julio, curso 2021/22 Agradecimientos Antes de presentar el trabajo que he realizado, me gustar´ıa utilizar esta p´agina para agradecer personalmente a todas las personas que lo han hecho posible. A mi tutor, Sergio Segura Rueda, por iniciarme en el mundo de la investigaci´on y la docencia que tanto me apasiona, por sus consejos y, en general, por ser el mejor tutor que se podr´ıa desear. Estoy deseando que continuemos las l´ıneas de investigaci´on derivadas de este proyecto y de mis trabajos previos para la realizaci´on de mi tesis doctoral. A todos los compa˜neros de mi grupo de investigaci´on, por hacerme sentir como uno m´as desde el primer d´ıa, especialmente a aquellos que me han acompa˜nado y aconsejado durante mi primer a˜no como profesor, y a los dem´as estudiantes de doctorado de la l´ınea de pruebas autom´aticas, Alberto Mart´ın y Giuliano Mirabella. A mis padres y a mi abuela, por su apoyo incondicional durante toda mi vida y por hacer posible mi carrera acad´emica. A mi grupo de amigos, Ram´on, Roberto, Samuel, Emilee, Emily, Julio y Peter, por darme ´animos en los momentos m´as duros de este a˜no tan peculiar y por su paciencia por mi poca disponibilidad en estos ´ultimos meses. A mis compa˜neros del grado y del m´aster, con los que me alegro de poder mantener todav´ıa una relaci´on de amistad: Alex, Vanessa, Gabie, David Romero, Germ´an, Jos´e Sandoval, Roberto, Chus, David Gil, Pablo Garc´ıa y Dani. A todos vosotros, muchas gracias. i Resumen La generaci´on autom´atica de casos de prueba para APIs RESTful es un ´area de investigaci´on en auge, dado el rol que desempe˜nan en la integraci´on software. La mayor´ıa de las propuestas en este ´ambito siguen un enfoque de caja negra, en el que los casos de prueba se generan autom´aticamente a partir de la especificaci´on de la API. A pesar de los prometedores resultados de estas propuestas, todas se encuentran limitadas por los tipos de errores que pueden detectar: fallos de servidor no controlados (etiquetados con un c´odigo 500) y disconformidades con la especificaci´on de la API. La falta de t´ecnicas para la detecci´on de errores espec´ıficos del dominio de cada API supone una importante limitaci´on para el grado de automatizaci´on obtenido por estas herramientas. En este trabajo, se propone un enfoque basado en la detecci´on de invariantes (i.e., propiedades que siempre se cumplen en uno o m´as puntos de la ejecuci´on de un programa) para automatizar el proceso de generaci´on de or´aculos de prueba, que pueden ser utilizados para la creaci´on de assertions. En concreto, la propuesta recibe como entrada la especificaci´on de la API y un conjunto de pruebas generadas autom´aticamente (para las que se conocen ´unicamente los valores de las entradas y la salida devuelta) y devuelve un conjunto de invariantes que pueden ser utilizados como or´aculos. Los resultados obtenidos en una evaluaci´on realizada sobre un conjunto de 8 operaciones de 6 APIs comerciales muestran la capacidad de la propuesta para generar cientos de or´aculos v´alidos, llegando a obtener una precisi´on del 100 % (y una precisi´on total del 66.5 %) y detectando un total de 6 bugs replicables en 4 operaciones pertenecientes a 3 sistemas con millones de usuarios (Amadeus Hotel, GitHub y OMDb). Palabras clave: APIs RESTful, Problema del or´aculo, Detecci´on de invariantes ii Abstract Automated test case generation for RESTful APIs is a thriving research topic, given the key role they play in software integration. Most proposals in this area follow a black-box approach, where test cases are automatically generated from an API specification. Despite the promising results of these proposals, they are all limited by the types of errors they can detect: uncontrolled server failures (labelled with a 500 status code) and disconformities with the API specification. The lack of domain-specific error detection techniques for each API is a major limitation to the degree of automation obtained by these tools. In this paper, we propose an approach based on the detection of invariants (i.e., properties that are always satisfied at one or more points in the execution of a program) to automate the process of generating test oracles, which can be used for the creation of assertions. Specifically, the proposal receives as input the API specification and a set of automatically generated tests (for which only the values of the inputs and the returned output are known) and returns a set of invariants that can be used as oracles. The results obtained in an evaluation performed on a set of 8 operations from 6 commercial APIs show the capability of the proposal to generate hundreds of valid oracles, obtaining an accuracy of up to 100 % (and a total accuracy of 66.5 %) and detecting a total of 6 reproducible bugs in 4 operations belonging to 3 systems with millions of users (Amadeus Hotel, GitHub and OMDb). Keywords: RESTful APIs, Oracle problem, Invariant detection iii ´ Indice general 1. Introducci´on 1 1.1. Problema .................................. 2 1.2. Contribuci´on ................................ 3 1.3. Metodolog´ıa................................. 4 1.4. Estructura de este documento . . . . . . . . . . . . . . . . . . . . . . . 5 2. Contexto 6 2.1. APIsRESTful................................ 6 2.1.1. Especificaci´on OAS . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2. Detecci´on de invariantes . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2.1. Daikon................................ 9 3. Estudio del estado del arte 13 4. Descripci´on de la propuesta 15 4.1. Instrumenter desarrollado . . . . . . . . . . . . . . . . . . . . . . . . . 15 4.1.1. Generaci´on del DeclsFile . . . . . . . . . . . . . . . . . . . . . . 15 4.1.2. Generaci´on del DtraceFile . . . . . . . . . . . . . . . . . . . . . 22 4.2. Modificaciones realizadas sobre Daikon . . . . . . . . . . . . . . . . . . 26 4.2.1. Modificaciones realizadas sobre el conjunto de invariantes . . . . 27 4.2.2. Supresores de invariantes . . . . . . . . . . . . . . . . . . . . . . 33 4.2.3. Supresi´on de variables derivadas . . . . . . . . . . . . . . . . . . 34 5. Validaci´on 35 5.1. Preguntas de investigaci´on . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.2. Dise˜no experimental . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.3. Resultados experimentales . . . . . . . . . . . . . . . . . . . . . . . . . 37 5.3.1. Efectividad para la generaci´on de or´aculos de prueba . . . . . . 37 5.3.2. Impacto del tama˜no del conjunto de pruebas en la calidad de los or´aculosgenerados ......................... 41 5.3.3. Capacidad de detecci´on de errores de la propuesta . . . . . . . . 42 6. Amenazas a la validez 46 6.1. Validezinterna ............................... 46 6.2. Validezexterna ............................... 47 7. Conclusiones y trabajo futuro 48 A. Bibliograf´ıa 49 iv ´ Indice de figuras 2.1. Diagrama del funcionamiento de una API RESTful . . . . . . . . . . . 6 4.1. Diagrama del funcionamiento de la propuesta . . . . . . . . . . . . . . 15 5.1. Distribuci´on de los Falsos Positivos . . . . . . . . . . . . . . . . . . . . 39 5.2. Evoluci´on de la precisi´on de la propuesta . . . . . . . . . . . . . . . . . 41 5.3. Confirmaci´on del error por parte de los proveedores de la API de GitHub. 44 5.4. Par´ametro type de la API de OMDb. . . . . . . . . . . . . . . . . . . . 44 v ´ Indice de extractos de c´odigo 1.1. Ejemplo de respuesta de Spotify. . . . . . . . . . . . . . . . . . . . . . . 3 2.1. OASSpotify. ................................ 8 2.2. Atributos de la clase StackAr . . . . . . . . . . . . . . . . . . . . . . . 11 2.3. M´etodos de la clase StackAr . . . . . . . . . . . . . . . . . . . . . . . . 11 2.4. Invariantes del objeto StackAr . . . . . . . . . . . . . . . . . . . . . . . 11 2.5. Invariantes del constructor de StackAr . . . . . . . . . . . . . . . . . . 12 2.6. Invariantes del m´etodo isFull . . . . . . . . . . . . . . . . . . . . . . . . 12 4.1. Versi´on reducida de la especificaci´on OAS de Spotify. . . . . . . . . . . 16 4.2. Ejemplo de respuesta en formato JSON. . . . . . . . . . . . . . . . . . 17 4.3. Estructura del DeclsFile. . . . . . . . . . . . . . . . . . . . . . . . . . . 17 4.4. Ejemplo de program point de entrada. . . . . . . . . . . . . . . . . . . 18 4.5. Ejemplo de program point de salida. . . . . . . . . . . . . . . . . . . . 19 4.6. Segundo nivel de anidamiento de la salida. . . . . . . . . . . . . . . . . 21 4.7. Ejemplo de program point de entrada en el DtraceFile. . . . . . . . . . 22 4.8. Estructura del DtraceFile. . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.9. Primer nivel de anidamiento de la salida. . . . . . . . . . . . . . . . . . 23 4.10. Segundo nivel de anidamiento de la salida. . . . . . . . . . . . . . . . . 24 4.11. Tercer nivel de anidamiento de la salida. . . . . . . . . . . . . . . . . . 25 4.12. Invariantes detectados para la ejemplificaci´on de la propuesta. . . . . . 26 4.13. Constructor y m´etodo get proto . . . . . . . . . . . . . . . . . . . . . . 31 4.14. M´etodos check modified y add modified . . . . . . . . . . . . . . . . . . 32 4.15. M´etodo format using . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 4.16. M´etodo isSameFormula . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 4.17. M´etodo computeConfidence . . . . . . . . . . . . . . . . . . . . . . . . 33 4.18. M´etodo get ni suppressions . . . . . . . . . . . . . . . . . . . . . . . . . 33 5.1. Fragmento de la especificaci´on OAS de Amadeus Hotel. . . . . . . . . . 42 5.2. Respuesta con 0 camas. . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 vi 1. Introducci´on En la actualidad, es habitual que distintas aplicaciones software (en muchas ocasiones pertenecientes a distintos proveedores) se comuniquen entre s´ı a trav´es de Internet. Esta comunicaci´on tiene lugar generalmente mediante el uso de las conocidas como APIs web (Web Application Programming Interfaces). La mayor´ıa de las APIs web actuales siguen el estilo arquitect´onico REST (RE- presentational State Transfer) [45], siendo conocidas como APIs RESTful. Las APIs RESTful se han convertido en el est´andar de-facto para la integraci´on web, compa˜n´ıas como Google, Amazon o Microsoft utilizan APIs RESTful para proveer la mayor´ıa de sus servicios. Es muy com´un que estas compa˜n´ıas, as´ı como desarrolladores independientes, ofrezcan acceso p´ublico a sus APIs para permitir su integraci´on en aplicaciones de terceros. En la pr´actica, esto permite incluir en nuestras aplicaciones funcionalidades tan diversas como comprobar los resultados de un partido de f´utbol (BeSoccer API [6]), escribir un tweet (Twitter API [29]), reservar una habitaci´on de hotel (Amadeus API [4]), traducir un texto (DeepL API [9]), o encontrar una ruta entre dos ubicaciones (Openroute API [18]). El amplio cat´alogo de APIs de distintas categor´ıas disponibles en repositorios como Programmable web [19] y RapidAPI [20] (que actualmente alojan 24K y 30K APIs respectivamente), reflejan la importancia del papel de las APIs RESTful en la integraci´on web. Dada la importancia del rol que desempe˜nan las APIs RESTful en la integraci´on web, resulta vital probarlas de forma exhaustiva, ya que un fallo en una sola API puede tener consecuencias en todos los sistemas que la integran. Afortunadamente, todas las APIs RESTful siguen unas mismas directrices de dise˜no claramente definidas, por lo que en muchos casos una misma t´ecnica para la generaci´on de pruebas pueda ser aplicada a cualquier API, independientemente de la tecnolog´ıa usada para su implementaci´on. Por estas razones, en a˜nos recientes han proliferado una gran cantidad de t´ecnicas orientadas al testing autom´atico de APIs RESTful. Estas t´ecnicas generan casos de prueba autom´aticamente a partir de una especificaci´on OAS (OpenAPI Specification) [17], que describe la funcionalidad de una API en t´erminos de las operaciones que soporta, los par´ametros que recibe y los formatos de respuesta. Sin embargo, tal y como se se˜nala en un estudio comparativo de herramientas centradas en la generaci´on autom´atica de pruebas para APIs RESTful publicado recientemente [51], una de las principales limitaciones compartida por todas las herramientas que conforman el estado del arte reside en el tipo de errores que pueden detectar: fallos inesperados (i.e., fallos del servidor etiquetados con un c´odigo 500), y errores de conformidad con la especificaci´on OAS (por ejemplo, que el valor de un campo de la respuesta que deber´ıa ser de tipo num´erico sea un string). No obstante, pueden existir toda clase de inconsistencias en respuestas aparentemente v´alidas que no ser´ıan detectadas por ninguna de estas propuestas. Por ejemplo, si un campo de la respuesta que indique la edad de un usuario tiene un valor negativo, si un string que representa la URL de un repositorio no es una URL v´alida o si al realizar una b´usqueda de ubicaciones filtrando por pa´ıses las ubicaciones devueltas no pertenecen al pa´ıs indicado como par´ametro. 1 1.1. Problema Determinar si la respuesta devuelta por una API es correcta o no constituye un desaf´ıo de gran complejidad, ya que en la mayor´ıa de las ocasiones no es posible conocer su salida esperada, esto es lo que se conoce como el problema del or´aculo [39]. Tomemos como ejemplo la operaci´on “Get Album tracks” de la API de Spotify. Esta operaci´on recibe como par´ametro el id de un ´album de Spotify (par´ametro id) y el n´umero m´aximo de resultados a devolver (par´ametro limit). Tras procesar estos par´ametros, la API devuelve una respuesta similar a la mostrada en el Extracto de c´odigo 1.1. A partir de la especificaci´on OAS y sin poseer ning´un conocimiento del dominio, no podemos detectar autom´aticamente si la respuesta es correcta m´as all´a de comprobar si cada campo de la respuesta es del tipo indicado (por ejemplo, href debe ser de tipo string). Sin embargo, observando esta respuesta, podemos pensar en algunos or´aculos que deber´ıan cumplirse para todas las respuestas de la API: Los valores de los campos limit (n´umero m´aximo de resultados que pueden devolverse) y total (n´umero total de canciones del album) deben ser mayores o iguales que el n´umero de resultados devueltos por la API (i.e., el tama˜no del array items). Asimismo, tampoco pueden ser negativos. El valor del campo limit debe ser igual que el valor seleccionado para el par´ametro limit al hacer la llamada a la API. Ciertos par´ametros de tipo string deben tener un formato espec´ıfico. Este es el caso, por ejemplo, de las URLs (campos href, y preview url), los ids de bases de datos (como los campos id de cada objeto de tipo “track” o “artist”) o los c´odigos de pa´ıses (todos los elementos del array de strings available markets deber´ıan tener longitud 2). El campo type siempre debe tener un valor espec´ıfico (“artist” para el objeto de tipo artista y “track” para el objeto de tipo canci´on). Toda canci´on deber´ıa tener al menos un artista. En otras palabras, el n´umero de elementos de la propiedad artists deber´ıa ser siempre mayor o igual que 1. 2 Si el cliente introduce un id de un ´album que est´e presente en la base de datos de Spotify, la API devolver´a una respuesta en formato JSON etiquetada con un c´odigo 200 y con la estructura indicada en las l´ıneas 39-100. Esta respuesta ser´a de tipo objeto (l´ınea 40) y contendr´a propiedades con informaci´on general, como una URL e informaci´on sobre paginaci´on y el n´umero total de resultados (l´ıneas 42-50), as´ı como la propiedad de tipo array de objetos items, cada uno de los elementos de este array ser´a una de las canciones que componen el ´album. N´otese que, para cada par´ametro y campo de la respuesta, la especificaci´on OAS indica su tipo de dato (string, boolean, integer, double, object o array). 2.2. Detecci´on de invariantes Un invariante es una propiedad que se cumple siempre en un punto o en una serie de puntos de la ejecuci´on de un programa [44]. Por ejemplo, en una funci´on que recibe como entrada una lista y le a˜nade un elemento, un posible invariante ser´ıa que la lista devuelta siempre tiene un mayor tama˜no que la que se pasa como par´ametro. Existen dos corrientes principales para la detecci´on autom´atica de invariantes: An´alisis est´atico del c´odigo: Consiste en detectar invariantes en un programa bas´andose ´unicamente en un an´alisis de su c´odigo fuente, sin ejecutarlo [41,47]. Generalmente, este enfoque se caracteriza por generar invariantes s´olidos y con una baja proporci´on de falsos positivos. Sin embargo, su principal desventaja reside en la limitada cantidad de invariantes que puede generar, que suelen ser muy poco espec´ıficos, dificultando el discernir distintos estados del programa. An´alisis de la ejecuci´on del programa: Tambi´en conocido como detecci´on din´amica de invariantes, consiste en detectar invariantes a partir de la ejecuci´on de un programa [43,48,42]. Generalmente, este acercamiento permite detectar una mayor cantidad de invariantes que un an´alisis est´atico, con la desventaja de que los invariantes producidos se conocen como invariantes potenciales que se cumplen para las ejecuciones del programa bajo an´alisis, pero que podr´ıan no cumplirse para otras ejecuciones en las que, por ejemplo, se usen otros valores para los par´ametros de entrada, lo que resulta en un mayor riesgo de falsos positivos. 2.2.1. Daikon Daikon [43] es una herramienta de detecci´on din´amica de invariantes que detecta la existencia de invariantes potenciales estad´ısticamente justificados mediante la instrumentalizaci´on del programa que se est´a ejecutando. Daikon posee una serie de caracter´ısticas que permiten su aplicaci´on en toda clase de contextos: Soporte para diferentes lenguajes de programaci´on y otros formatos de datos: Daikon puede detectar invariantes potenciales en programas escritos en distintos lenguajes de programaci´on, adem´as de soportar distintos formatos de salida que permiten, por ejemplo, generar assertions autom´aticamente para programas escritos en Java. 9 Salidas expresivas y personalizables: La ´ultima versi´on de Daikon (lanzada en Noviembre de 2021) soporta un total de 143 invariantes distintos, algunos ejemplos son valores constantes (x=a), valores distintos de cero (x= 0), valores dentro de un rango (a≤x≤b), relaciones lineales (y=ax +b), ordenaci´on (x≤y) o subconjuntos (x∈y), entre otros. Cabe destacar que esta lista de invariantes es personalizable, ya que Daikon permite al usuario deshabilitar aquellos invariantes que puedan resultar en falsos positivos (o que no aporten informaci´on relevante en el contexto del problema a resolver), as´ı como extender el sistema definiendo nuevos tipos de invariantes. Daikon tambi´en puede calcular invariantes sobre variables derivadas (que tambi´en pueden ser deshabilitadas y definidas por el usuario), como puede ser el tama˜no (size) de las propiedades de tipo array. Escalabilidad: A pesar de soportar un amplio cat´alogo de invariantes y variables derivadas, Daikon es escalable a programas no triviales. Filtrado de invariantes: Al analizar la traza de un programa para detectar invariantes, existe el riesgo de reportar invariantes redundantes o que se han cumplido por pura coincidencia. •Para mitigar la posibilidad de reportar invariantes que hayan podido darse por casualidad, Daikon calcula, para cada invariante, una m´etrica de fiabilidad (confidence) en base al n´umero de ocasiones en las que se ha encontrado el invariante, de manera que el invariante sea reportado si y solo si se ha superado un umbral de fiabilidad m´ınimo (M´as detalles sobre el c´alculo de esta m´etrica en la secci´on 4.2.1). •Con el objetivo de filtrar los invariantes redundantes, Daikon permite especificar que determinados invariantes est´an l´ogicamente implicados por otros (por ejemplo, el invariante this.age is Positive est´a l´ogicamente implicado por el invariante this.age>=18), de esta forma los invariantes redundantes no solo no ser´an reportados por Daikon, sino que tampoco son calculados, lo que optimiza considerablemente el rendimiento de la herramienta. Instrumenter Un instrumenter es una herramienta que convierte un conjunto de datos, generalmente una traza de ejecuci´on de un programa escrito en un lenguaje de programaci´on determinado, en un formato que puede ser utilizado como input para Daikon [7] (este proceso se conoce como instrumentalizaci´on). En concreto, un instrumenter genera los siguientes archivos: DeclsFile: Con extensi´on .decls, especifica la estructura de todos los puntos del programa (program points), entre los que se incluyen todos los objetos y las entradas y las salidas de todos los m´etodos y funciones. DtraceFile: Con extensi´on .dtrace, especifica los valores asignados a cada punto del programa definido en el DeclsFile. Los intrumenters son espec´ıficos del lenguaje de programaci´on. Actualmente, existen instrumenters de Daikon para Java, C++, .NET, Eiffel, Perl y archivos en formato 10 CSV [32], entre otros. Ejemplo de uso Para ilustrar el funcionamiento de Daikon, tomemos como ejemplo una clase StackAr.java que implemente una pila con un tama˜no m´aximo (pasado como par´ametro al crear la pila) y que tenga los atributos del Extracto de c´odigo 2.2 e implemente los m´etodos del Extracto 2.3. 1Object[] theArray; // Array que contiene los elementos de la pila 2int topOfStack; // Indice del elemento superior de la pila. -1 si la no contiene elementos Extracto de c´odigo 2.2: Atributos de la clase StackAr Supongamos que existe una clase StackArTester.java con un m´etodo main en el que se crean varias pilas y se ejecutan varias operaciones sobre ellas. En el contexto de detecci´on de invariantes en pruebas software, podr´ıamos entender la clase StackAr.java como el software que queremos probar y la clase StackArTester.java como un conjunto de pruebas. 1void push(Object x) // Insertar elemento x 2void pop() // Extrae el ultimo elemento insertado 3Object top() // Devuelve el ultimo elemento insertado 4Object topAndPop() // Extrae y devuelve el ultimo elemento insertado 5boolean isEmpty() // Devuelve true si la pila est´ a vacia, false en caso contario 6boolean isFull() // Devuelve true si la pila est´ a llena, false en caso contrario 7void makeEmpty() // Elimina todos los elementos de la pila Extracto de c´odigo 2.3: M´etodos de la clase StackAr Para detectar invariantes en este programa, comenzamos ejecutando un instrumenter sobre ´el para obtener un DeclsFile y un DtraceFile. Para instrumentalizar un programa escrito en Java, usar´ıamos el instrumenter Chicory [32], que crea program points para cada objeto y para la entrada y salida de cada m´etodo. Una vez disponemos de los archivos .decls y.dtrace, los usamos como inputs para Daikon, que nos devolver´a una serie de invariantes para cada program point. Por ejemplo, el Extracto de c´odigo 2.4 muestra algunos de los invariantes que se han cumplido en todo momento para todas las instancias del objeto StackAr. En concreto, Daikon ha detectado que el valor del atributo topOfStack debe ser mayor o igual que -1 (valdr´a -1 cuando la pila est´e vac´ıa, condici´on que se cumple al menos en el momento de su creaci´on) y menor estricto que el tama˜no del array que representa la pila (al crear una nueva pila, el array tendr´a un tama˜no igual a la capacidad que se introduce como par´ametro, con todos sus elementos siendo null). 1this.topOfStack >= -1 2this.topOfStack < this.theArray.length Extracto de c´odigo 2.4: Invariantes del objeto StackAr Por otra parte, el Extracto 2.5 muestra las precondiciones y postcondiciones del constructor de StackAr.java, que indican que el valor del par´ametro que especifica el tama˜no de la pila debe ser mayor o igual que cero (l´ınea 2), que el tama˜no del array que representa la pila ser´a igual a la capacidad (l´ınea 4), que el valor inicial del 11 atributo topOfStack ser´a siempre -1 (l´ınea 5) y que todos los elementos del array ser´an inicialmente nulos (l´ınea 6). 1// Precondiciones del constructor de StackAr 2capacity >= 0 3// Postcondiciones del constructor de StackAr 4orig(capacity) == this.theArray.length 5this.topOfStack == -1 6this.theArray[] elements == null Extracto de c´odigo 2.5: Invariantes del constructor de StackAr Finalmente, el Extracto 2.6 contiene los invariantes detectados en la salida del m´etodo isFull. Daikon ha detectado autom´aticamente que ninguno de los atributos del objecto StackAr es modificado (l´ıneas 1-3) y que el m´etodo devuelve false si la pila no est´a llena (l´ınea 4), y true en caso contrario (l´ınea 5). 1this.theArray == orig(this.theArray) 2this.theArray[] == orig(this.theArray[]) 3this.topOfStack == orig(this.topOfStack) 4(return == false) <==> (this.topOfStack < this.theArray.length - 1) 5(return == true) <==> (this.topOfStack == this.theArray.length - 1) Extracto de c´odigo 2.6: Invariantes del m´etodo isFull 12 3. Estudio del estado del arte Este trabajo se encuentra dentro del marco del testing autom´atico de APIs REST- ful. En los ´ultimos a˜nos, han proliferado una gran cantidad de herramientas y t´ecnicas orientadas a incrementar esta automatizaci´on, que pueden dividirse dentro de dos grandes categor´ıas: de caja blanca y de caja negra. Al requerir de acceso al c´odigo fuente del sistema, las t´ecnicas de caja blanca son menos comunes. Arcuri [37] propuso una t´ecnica que genera casos de prueba mediante algoritmos gen´eticos que busca maximizar la cobertura de c´odigo obtenida y el n´umero de fallos encontrados. Por otra parte, las t´ecnicas de caja negra utilizan la informaci´on de la especificaci´on OAS para generar casos de prueba, sin necesidad de acceder al c´odigo fuente, lo que permite que sean aplicables a cualquier API, independientemente de la tecnolog´ıa usada para su implementaci´on. Estas propuestas generan casos de prueba mediante la aplicaci´on de diferentes t´ecnicas como testing basado en modelos [52,59] o en propiedades [49]. Otras propuestas buscan generar secuencias de operaciones de manera autom´atica que se infieren analizando los nombres de los par´ametros y los campos de la respuesta [38] o creando un grafo de dependencias entre operaciones [60]. Tambi´en existen propuestas que generan valores de entrada de manera autom´atica [34]. Sin embargo, tal y como se˜nala un estudio comparativo publicado recientemente [51], todas estas herramientas se encuentran limitadas por el tipo de errores que pueden detectar. La Tabla 3.1 muestra los or´aculos utilizados por las 10 principales herramientas de generaci´on autom´atica de pruebas para APIs RESTful que existen en la actualidad. Herramienta Or´aculos utilizados Sitio web EvoMasterWB C´odigo de estado [2] EvoMasterBB C´odigo de estado [2] RESTler C´odigo de estado y verificaciones pre-definidas [23] RestTestGen C´odigo de estado y validaci´on del formato de la respuesta [24] RESTest C´odigo de estado y validaci´on del formato de la respuesta [22] Schemathesis C´odigo de estado y validaci´on del formato de la respuesta [25] Dredd C´odigo de estado y validaci´on del formato de la respuesta [10] Tcases C´odigo de estado [28] bBoxRT C´odigo de estado y an´alisis del comportamiento [5] APIFuzzer C´odigo de estado [1] Tabla 3.1: Or´aculos utilizados por las principales herramientas de generaci´on de pruebas para APIs RESTful. Fuente: [51] Esta tabla revela que, aunque se han producido una gran cantidad de avances en generaci´on autom´atica de pruebas para este tipo de sistemas, actualmente sigue existiendo una importante limitaci´on en lo que respecta a la generaci´on autom´atica de or´aculos de prueba, lo que limita considerablemente la capacidad de detecci´on de errores de estas t´ecnicas. Algunos autores han propuesto t´ecnicas basadas en el uso de pruebas metam´orficas [58]. A pesar de su potencial y capacidad demostrada de detectar 13 errores en APIs comerciales como Spotify y YouTube, estas t´ecnicas solo son aplicables a determinadas operaciones de b´usqueda, adem´as de requerir de especificaciones generadas manualmente. Por otra parte, la detecci´on autom´atica de invariantes ha demostrado ser de gran utilidad en contextos tan variados como bases de datos relacionales [40], el mercado de valores [32], aplicaciones web [53], reparar vulnerabilidades en programas [62], sistemas ciberf´ısicos [61,33] o rob´otica [50], entre otros. Adem´as, los invariantes detectados por herramientas como Daikon han sido utilizados por varios autores para comprobar el impacto de la introducci´on de mutantes en un software [57], validaci´on de inputs [54], y como feedback para fuzzers en casos en los que la cobertura de c´odigo resultaba insuficiente como m´etrica para validar la calidad de un conjunto de pruebas [46]. Sin embargo, esta t´ecnica a´un no ha sido aplicada en el contexto de generaci´on de pruebas en APIs RESTful, a pesar de su potencial. 14 4. Descripci´on de la propuesta El principal objetivo de este proyecto es desarrollar un sistema de generaci´on autom´atica de or´aculos de prueba en el contexto de las pruebas de caja negra de APIs RESTful mediante un proceso de detecci´on de invariantes potenciales. Para ello, se ha desarrollado un instrumenter para Daikon que recibe como entrada la especificaci´on OAS de una API RESTful (permitiendo extraer los formatos de entrada y salida de cada operaci´on) y un conjunto de pruebas en formato CSV en el que cada instancia representa una prueba y contiene los valores usados para los par´ametros de entrada y el cuerpo de la respuesta en formato JSON. El instrumenter desarrollado procesa la especificaci´on y las pruebas, generando una instrumentalizaci´on del conjunto de pruebas consistente en un DeclsFile y un DtraceFile que son utilizados como entradas para una versi´on modificada de Daikon que devuelve una lista de invariantes potenciales que pueden ser utilizados como or´aculos. El procedimiento completo de la propuesta es el mostrado en la Figura 4.1. Figura 4.1: Diagrama del funcionamiento de la propuesta En las siguientes secciones se detallan los detalles de dise˜no e implementaci´on del instrumenter desarrollado, as´ı como las modificaciones realizadas sobre el c´odigo de Daikon. 4.1. Instrumenter desarrollado 4.1.1. Generaci´on del DeclsFile Como se ha mencionado en la Secci´on 2.2, un DeclsFile especifica la estructura de todos los puntos de un programa, entre los que se incluyen las clases, los objetos, las entradas y salidas de todos los m´etodos y funciones que lo componen. Al estar trabajando en un contexto de pruebas de caja negra en el que no tenemos acceso al c´odigo fuente de la API, los ´unicos tipos de program points devueltos por el instrumenter ser´an aquellos que modelen el formato de las entradas (ENTER) y de las salidas (EXIT). 15 Para explicar de manera detallada la estructura de un DeclsFile y ejemplificar el proceso de instrumentalizaci´on sin mostrar informaci´on redundante, se utilizar´a como ejemplo una versi´on simplificada de la operaci´on “Get Album Tracks” de la API de Spotify, mostrada en el Extracto 4.1. 1paths: 2’/albums/{id}/tracks’: 3get: 4operationId:’getAlbumTracks’ 5parameters: 6-name: id 7description: | 8’The Spotify ID for the album’ 9in: path 10 required:true 11 type: string 12 -name: market 13 description: | 14 ’An ISO 3166-1 alpha-2 country code’ 15 in: query 16 required:false 17 type: string 18 -name: limit 19 description: | 20 ’The maximum number of items to return’ 21 in: query 22 required:false 23 type: integer 24 responses: 25 ’200’: 26 description:’OK’ 27 schema: 28 type: object 29 properties: 30 total: 31 type: integer 32 href: 33 type: string 34 items:# Array de objetos 35 type: array 36 items: 37 type: object 38 properties: Extracto de c´odigo 4.1: Versi´on reducida de la especificaci´on OAS de Spotify. 39 artists:# Array de objetos 40 type: array 41 items: 42 type: object 43 properties: 44 id: 45 type: string 46 name: 47 type: string 48 # Array de strings 49 available_markets: 50 type: array 51 items: 52 type: string 53 id: 54 type: string 55 name: 56 type: string 57 track_number: 58 type: integer 59 explicit: 60 type: boolean 61 linked_from:# Objeto anidado 62 type: object 63 properties: 64 id: 65 type: string 66 uri: 67 type: string 68 ’400’: 69 description:’Invalid input’ 70 schema: 71 type: object 72 properties: 73 status: 74 type: integer 75 message: 76 type: string Esta operaci´on recibe par´ametros de distinto tipo (id ymarket son de tipo string, mientras que limit es num´erico) y que se env´ıan en distintas partes de la petici´on HTTP (path y query). Por otra parte, hay dos posibles tipos de respuesta, cada una con su correspondiente c´odigo de estado (l´ıneas 25 y 68). El formato de la respuesta con c´odigo 200 ilustra las caracter´ısticas m´as comunes de las respuestas devueltas por una API, al contener propiedades de distintos tipos de datos primitivos (num´ericos, strings y booleans), arrays de objetos (propiedad items en las l´ıneas 36-67, y propiedad artists en las l´ıneas 39-47), arrays de tipos primitivos (available markets, l´ıneas 49-52, es un array de strings) y objetos anidados dentro de objetos (cada elemento del array de objetos items, contiene como propiedad un objeto linked from, l´ıneas 61-67). El Extracto de c´odigo 4.2 muestra un JSON que sigue el formato de respuesta de las respuestas con c´odigo 200. 16 1{ 2"total":14, 3"href":"https://api.spotify.com/v1/albums/4Em5W5HgYEvhpc2elrpKES/tracks?limit=1&market=ES", 4"items": [ 5{ 6"artists": [ 7{ 8"id":"2CvCyf1gEVhI0mX6aFXmVI", 9"name":"Paul Simon" 10 }, 11 { 12 "id":"70cRZdQywnSFp9pnc2WTCE", 13 "name":"Arthur Garfunkel" 14 } 15 ], 16 "available_markets":["ES","US","JP" ], 17 "id":"0gFvkiT2afIcJwNxXQ7W51", 18 "name":"Mrs. Robinson", 19 "track_number":1, 20 "explicit":false, 21 "linked_from": { 22 "id":"98cZPdKywnMGp8fnw2XTYU", 23 "uri":"https://open.spotify.com/artist/98cZPdKywnMGp8fnw2XTYU" 24 } 25 } 26 ] 27 } Extracto de c´odigo 4.2: Ejemplo de respuesta en formato JSON. El instrumenter desarrollado emplear´ıa el OAS del Extracto 4.1 para generar un DeclsFile que contenga los formatos de entrada y salida de cada una de las operaciones. Dicho DeclsFile seguir´ıa la estructura mostrada en el Extracto 4.3. 1decl-version 2.0 2 3ppt main.albums{id}tracks.getAlbumTracks&200(main.getAlbumTracks&Input):::ENTER 4... 5 6ppt main.albums{id}tracks.getAlbumTracks&200&items(main.getAlbumTracks&Input):::ENTER 7... 8 9ppt main.albums{id}tracks.getAlbumTracks&200&items&artists(main.getAlbumTracks&Input):::ENTER 10 ... 11 12 ppt main.albums{id}tracks.getAlbumTracks&400(main.getAlbumTracks&Input):::ENTER 13 ... 14 15 ppt main.albums{id}tracks.getAlbumTracks&200(main.getAlbumTracks&Input):::EXIT1 16 ... 17 18 ppt main.albums{id}tracks.getAlbumTracks&200&items(main.getAlbumTracks&Input):::EXIT2 19 ... 20 21 ppt main.albums{id}tracks.getAlbumTracks&200&items&artists(main.getAlbumTracks&Input):::EXIT3 22 ... 23 24 ppt main.albums{id}tracks.getAlbumTracks&400(main.getAlbumTracks&Input):::EXIT4 25 ... Extracto de c´odigo 4.3: Estructura del DeclsFile. El DeclsFile comienza especificando la versi´on del formato del archivo (l´ınea 1), seguida de todos los program points que componen la API a instrumentalizar (en este caso, ser´an ´unicamente las entradas y las salidas). Existir´a, como m´ınimo, un program point de entrada y de salida por cada c´odigo de respuesta, por motivos que se explicar´an m´as adelante, cada propiedad de tipo array de objetos tendr´a sus propios program point 17 de entrada y de salida. Utilizar program points diferentes en funci´on del c´odigo de respuesta permite, entre otras cosas, detectar invariantes en las entradas que nos indiquen las razones por las que se ha obtenido un determinado tipo de respuesta. Por ejemplo, si la API de Spotify est´a bien implementada, deber´ıa devolver un error de validaci´on (c´odigo 400) si el usuario introduce valores inv´alidos para el par´ametro num´erico limit (0 o un valor negativo) o para los par´ametros de tipo string market oid (Por ejemplo, con una longitud incorrecta). De esta forma, para el program point que modela las entradas para las que se ha devuelto una salida v´alida (getAlbumTracks&200(main.getAlbumTracks&Input):::ENTER), se obtendr´an invariantes que modelar´an este comportamiento (input.limit >= 1, LENGTH(input.market)==2 yLENGTH(input.id)==22) que no necesariamente estar´an presentes en las entradas de las llamadas que han devuelto un error de validaci´on (getAlbumTracks&400(main.getAlbumTracks&Input):::ENTER). Los program points que representan entradas tendr´an exactamente la misma estructura, diferenci´andose ´unicamente por su nombre (l´ınea 1), el Extracto 4.4 muestra un ejemplo de program point de entrada. 1ppt main.albums{id}tracks.getAlbumTracks&200(main.getAlbumTracks&Input):::ENTER 2ppt-type enter 3variable input 4var-kind variable 5dec-type main.getAlbumTracks&Input 6rep-type hashcode 7variable input.id 8var-kind field id 9enclosing-var input 10 dec-type java.lang.String 11 rep-type java.lang.String 12 variable input.market 13 var-kind field market 14 enclosing-var input 15 dec-type java.lang.String 16 rep-type java.lang.String 17 variable input.limit 18 var-kind field limit 19 enclosing-var input 20 dec-type int 21 rep-type int Extracto de c´odigo 4.4: Ejemplo de program point de entrada. Cada program point comienza con la especificaci´on de su nombre (l´ınea 1) seguida del tipo de program point (l´ınea 2). A continuaci´on, se incluyen las declaraciones de todas las variables involucradas en el program point. En el Extracto 4.4 hay una variable de tipo objeto (input) que representa la entrada completa, con sus propiedades siendo los distintos par´ametros de entrada (input.id,input.market einput.limit). En el contexto de nuestra propuesta, cada variable de Daikon puede contener los siguientes campos: var-kind <kind>: Especifica el tipo de variable. En nuestro contexto, sus posibles valores son: •variable: Empleado en las variables de tipo objeto de las entradas. •return: Empleado en las variables de tipo objeto de las salidas. 18 1main.albums{id}tracks.getAlbumTracks&200&items&artists(main.getAlbumTracks&Input):::ENTER 2... 3 4main.albums{id}tracks.getAlbumTracks&200&items&artists(main.getAlbumTracks&Input):::EXIT3 5input 6... 7return 82043815652 91 10 return.id 11 "2CvCyf1gEVhI0mX6aFXmVI" 12 1 13 return.name 14 "Paul Simon" 15 1 16 17 main.albums{id}tracks.getAlbumTracks&200&items&artists(main.getAlbumTracks&Input):::ENTER 18 ... 19 20 main.albums{id}tracks.getAlbumTracks&200&items&artists(main.getAlbumTracks&Input):::EXIT3 21 input 22 ... 23 return 24 2043815652 25 1 26 return.id 27 "70cRZdQywnSFp9pnc2WTCE" 28 1 29 return.name 30 "Arthur Garfunkel" 31 1 Extracto de c´odigo 4.11: Tercer nivel de anidamiento de la salida. El Extracto de c´odigo 4.12 muestra algunos de los invariantes que Daikon detectar´ıa si lo ejecutasemos con un conjunto de pruebas como el de la ejemplificaci´on de la propuesta. 25 1=========================================================================== 2main.albums{id}tracks.getAlbumTracks&200(main.getAlbumTracks&Input):::ENTER 3LENGTH(input.id)==22 4input.limit >= 1 5LENGTH(input.market)==2 6=========================================================================== 7main.albums{id}tracks.getAlbumTracks&200(main.getAlbumTracks&Input):::EXIT 8LENGTH(input.market)==2 9return.href is Url 10 input.limit >= size(return.items[]) 11 return.total >= size(return.items[]) 12 return.total >= 1 13 =========================================================================== 14 main.albums{id}tracks.getAlbumTracks&200&items(main.getAlbumTracks&Input):::ENTER 15 ... 16 =========================================================================== 17 main.albums{id}tracks.getAlbumTracks&200&items(main.getAlbumTracks&Input):::EXIT 18 size(return.artists[]) >= 1 19 All the elements of return.available_markets[] have LENGTH=2 20 LENGTH(return.id)==22 21 return.track_number >= 1 22 LENGTH(return.href)==56 23 return.href is Url 24 LENGTH(return.linked_from.id)==22 25 return.linked_from.uri is Url 26 LENGTH(return.linked_from.uri)==54 27 =========================================================================== 28 main.albums{id}tracks.getAlbumTracks&200&items&artists(main.getAlbumTracks&Input):::ENTER 29 ... 30 =========================================================================== 31 main.albums{id}tracks.getAlbumTracks&200&items&artists(main.getAlbumTracks&Input):::EXIT 32 LENGTH(return.id)==22 Extracto de c´odigo 4.12: Invariantes detectados para la ejemplificaci´on de la propuesta. 4.2. Modificaciones realizadas sobre Daikon En esta secci´on se detallan las distintas modificaciones llevadas a cabo sobre el c´odigo de Daikon para adaptarlo al contexto de generaci´on de or´aculos de pruebas para APIs RESTful. En concreto, se han realizado los siguientes cambios: Se han deshabilitado algunos de los tipos de invariantes disponibles por defecto por no aportar informaci´on relevante en el contexto actual, ya que simplemente podr´ıan resultar en la aparici´on de falsos positivos (secci´on 4.2.1). Definici´on de nuevos tipos de invariantes aplicables en el contexto de las APIs RESTful (secci´on 4.2.1). Estos invariantes est´an basados en una publicaci´on previa del autor de este trabajo en el que se realiz´o una evaluaci´on con un conjunto de datos compuesto por 48 APIs reales [34]. Definici´on de nuevos supresores (un supresor indica que un tipo de invariante est´a implicado por otro) para evitar que Daikon genere or´aculos redundantes (secci´on 4.2.2). Supresi´on de variables derivadas que no aportan informaci´on valiosa en el contexto actual, evitando de esta forma que Daikon muestre informaci´on irrelevante (secci´on 4.2.3). 26 4.2.1. Modificaciones realizadas sobre el conjunto de invariantes En concreto, se han a˜nadido 22 invariantes nuevos propios del dominio de las APIs RESTful y se han deshabilitado 25 invariantes que no revelar´ıan informaci´on relevante sobre el funcionamiento de la API y probablemente resultar´ıan en la aparici´on de falsos positivos. Nuestra versi´on modificada de Daikon soporta un total de 140 invariantes, que pueden clasificarse dentro de las siguientes categor´ıas: Relaciones aritm´eticas: Indican comparaciones del valor de las propiedades de tipo num´erico. Por ejemplo, al realizar una b´usqueda de ´albumes en Spotify, el n´umero de track de una canci´on debe ser mayor o igual que 1 (return.track number >= 1). Propiedades de arrays: Indican que un array cumple con determinadas caracter´ısticas. Por ejemplo, al buscar hoteles por id en Amadeus, el id de cada hotel devuelto debe encontrarse dentro del array de ids que se utiliza como par´ametro de entrada (return.hotelId in input.hotelIds[]). Asimismo, el tama˜no de la propiedad de tipo array que contiene los resultados (data) debe ser me- nor o igual que el tama˜no de la lista de ids que se ha usado como par´ametro (size(input.hotelIds[]) >= size(return.data[])). Valores espec´ıficos: Indican que una propiedad siempre tiene un valor o conjunto de valores fijos. Por ejemplo, en la API de GitHub, un repositorio puede ser p´ublico o privado (return.visibility one of {"private", "public"}). Formatos espec´ıficos: Indican que un campo de tipo string siempre sigue un formato concreto, como URLs, fechas o emails. Por ejemplo, el campo Poster de OMDb siempre debe ser de tipo URL (return.Poster is Url). Invariantes deshabilitados En este apartado se enumeran los invariantes de Daikon que han sido deshabilitados por no aportar informaci´on relevante en el contexto que nos ocupa. La decisi´on de eliminar estos invariantes se tom´o tras una experimentaci´on preliminar que revel´o que generaban casi un 100 % de falsos positivos. Los invariantes deshabilitados son los siguientes [8]: 1. IntNonEqual: Indica que dos variables de tipo entero nunca son iguales. Se representa como x != y. 2. FloatNonEqual: Indica que dos variables de tipo double nunca son iguales. Se representa como x != y. 3. StringNonEqual: Indica que dos variables de tipo string nunca son iguales. Se representa como x != y. 4. StringLessThan: Indica que una variable de tipo string siempre tiene menos caracteres que una segunda variable del mismo tipo. Se representa como x < y. 27 5. StringGreaterThan: Indica que una variable de tipo string siempre tiene m´as caracteres que una segunda variable del mismo tipo. Se representa como x > y. 6. StringLessEqual: Indica que una variable de tipo string siempre tiene un n´umero menor o igual de caracteres que una segunda variable del mismo tipo. Se representa como x <= y. 7. StringGreaterEqual: Indica que una variable de tipo string siempre tiene un n´umero mayor o igual de caracteres que una segunda variable del mismo tipo. Se representa como x >= y. 8. SeqSeqStringLessThan: Indica que los elementos una variable de tipo array de strings siempre tienen menor longitud que los elementos de un segundo array del mismo tipo. Se representa como x[] < y[] lexically. 9. SeqSeqStringGreaterThan: Indica que los elementos una variable de tipo array de strings siempre tienen mayor longitud que los elementos de un segundo array del mismo tipo. Se representa como x[] > y[] lexically. 10. SeqSeqStringLessEqual: Indica que los elementos una variable de tipo array de strings siempre tienen una longitud menor o igual que los elementos de un segundo array del mismo tipo. Se representa como x[] <= y[] lexically. 11. SeqSeqStringGreaterEqual: Indica que los elementos una variable de tipo array de strings siempre tienen una longitud mayor o igual que los elementos de un segundo array del mismo tipo. Se representa como x[] >= y[] lexically. 12. PairwiseStringLessThan: Indica que la longitud de cada elemento i de una variable de tipo array de strings siempre es menor que la longitud del elemento i de un segundo array del mismo tipo. Se representa como x[] < y[]. 13. PairwiseStringGreaterThan: Indica que la longitud de cada elemento i de una variable de tipo array de strings siempre es mayor que la longitud del elemento i de un segundo array del mismo tipo. Se representa como x[] > y[]. 14. PairwiseStringLessEqual: Indica que la longitud de cada elemento i de una variable de tipo array de strings siempre es menor o igual que el elemento i de un segundo array del mismo tipo. Se representa como x[] <= y[]. 15. PairwiseStringGreaterEqual: Indica que la longitud de cada elemento i de una variable de tipo array de strings siempre es mayor o igual que el elemento i de un segundo array del mismo tipo. Se representa como x[] >= y[]. 16. NumericInt$Divides: Indica que una variable de tipo entero es divisible entre otra del mismo tipo. Se representa como x % y == 0. 17. NumericFloat$Divides: Indica que una variable de tipo double es divisible entre otra del mismo tipo. Se representa como x % y == 0. 18. NonZeroFloat: Indica que el valor de una variable de tipo double siempre es distinto de 0. Se representa como x != 0. 19. NonZero: Indica que el valor de una variable de tipo entero siempre es distinto de 0. Se representa como x != 0. 28 20. EltNonZeroFloat: Indica que todos los elementos de una variable de tipo array de doubles siempre son distintos de cero. Se representa como x[] elements != 0. 21. EltNonZero: Indica que todos los elementos de una variable de tipo array de enteros siempre son distintos de cero. Se representa como x[] elements != 0. 22. NumericInt$Square: Indica que el valor de una variable de tipo entero siempre es igual que el cuadrado del valor de otra variable del mismo tipo. Se representa como x = y**2. 23. NumericFloat$Square: Indica que el valor de una variable de tipo double siempre es igual que el cuadrado del valor de otra variable del mismo tipo. Se representa como x = y**2. 24. LinearTernary: Representa una relaci´on linear entre tres variables de tipo entero. Se representa como ax+by+cz+d=0. 25. LinearTernaryFloat: Representa una relaci´on linear entre tres variables de tipo double. Se representa como ax+by+cz+d=0. Nuevos invariantes En este apartado se especifican los nuevos invariantes definidos para el contexto de este trabajo, as´ı como el procedimiento seguido para definir un nuevo invariante en Daikon. Para la definici´on de este conjunto de invariantes, se ha realizado un an´alisis de un conjunto de 48 APIs reales seleccionado de forma sistem´atica por el autor para una publicaci´on anterior [34]. Los invariantes definidos son los siguientes: 1. IsUrl: Indica que el valor de una variable de tipo string siempre es una URL. Se representa como x is Url. 2. SequenceStringElementsAreUrl: Indica que todos los elementos de un array de strings son URLs. Se representa como All the elements of x are URLs. 3. FixedLengthString: Indica que el valor de una variable de tipo string siempre tiene una longitud fija n. Se representa como LENGTH(x)==n. 4. SequenceFixedLengthString: Indica que todos los elementos de un array de strings tienen una longitud fija n. Se representa como All the elements of x have LENGTH=n. 5. IsNumeric: Indica que los caracteres de una variable de tipo string siempre son num´ericos. Se representa como x is Numeric. 6. SequenceStringElementsAreNumeric: Indica que los caracteres de todos los elementos de un array de strings son siempre num´ericos. Se representa como All the elements of x are Numeric. 7. IsEmail: Indica que el valor de una variable de tipo string siempre es un email. Se representa como x is Email. 29 8. SequenceStringElementsAreEmail: Indica que todos los elementos de un array de strings son emails. Se representa como All the elements of x are emails. 9. IsDateDDMMYYYY: Indica que el valor de una variable de tipo string es siempre una fecha que sigue el formato DD/MM/YYYY (el separador puede ser el caracter “/” o “-”). Se representa como x is a Date. Format: DD/MM/YYYY. 10. SequenceStringElementsAreDateDDMMYYYY: Indica que todos los elementos de un array de strings son fechas que siguen el formato DD/MM/YYYY. Se representa como All the elements of x are dates. Format: DD/MM/YYYY. 11. IsDateMMDDYYYY: Indica que el valor de una variable de tipo string es siempre una fecha que sigue el formato MM/DD/YYYY (el separador puede ser el caracter “/” o “-”). Se representa como x is a Date. Format: MM/DD/YYYY. 12. SequenceStringElementsAreDateMMDDYYYY: Indica que todos los elementos de un array de strings son fechas que siguen el formato MM/DD/YYYY. Se representa como All the elements of x are dates. Format: MM/DD/YYYY. 13. IsDateYYYYMMDD: Indica que el valor de una variable de tipo string es siempre una fecha que sigue el formato YYYY/MM/DD (el separador puede ser el caracter “/” o “-”). Se representa como x is a Date. Format: YYYY/MM/DD. 14. SequenceStringElementsAreDateYYYYMMDD: Indica que todos los elementos de un array de strings son fechas que siguen el formato YYYY/MM/DD. Se representa como All the elements of x are dates. Format: YYYY/MM/DD. 15. IsHour: Indica que el valor de una variable de tipo string es siempre una hora en formato de 24 horas. Se representa como x is Hour: HH:MM 24-hour format, optional leading 0. 16. SequenceStringElementsAreHour: Indica que todos los elementos de un array de strings son horas en formato de 24 horas. Se representa como All the elements of x are Hours: HH:MM 24-hour format, optional leading 0. 17. IsHourAMPM: Indica que el valor de una variable de tipo string es siempre una hora en formato de 12 horas. Se representa como x is Hour: HH:MM 12-hour format, optional leading 0. 18. SequenceStringElementsAreHourAMPM: Indica que todos los elementos de un array de strings son horas en formato de 12 horas. Se representa como All the elements of x are Hours: HH:MM 12-hour format, optional leading 0, mandatory meridiems (AM/PM). 19. IsHourWithSeconds: Indica que el valor de una variable de tipo string es siempre una hora en formato de 24 horas, con los segundos incluidos. Se representa como x is Hour: HH:MM:SS 24-hour format with optional leading 0. 20. SequenceStringElementsAreHourWithSeconds: Indica que todos los elementos de un array de strings son horas en formato de 24 horas, con los segundos incluidos. Se representa como All the elements of x are Hours: HH:MM:SS 24-hour format with optional leading 0. 30 21. IsTimestampYYYYMMHHThhmmssmm: Indica que el valor de una variable de tipo string es siempre un timestamp. Se representa como x is Timestamp. Format: YYYY-MM-DDTHH:MM:SS.mmZ (Miliseconds are optional). 22. SequenceStringElementsAreTimestampYYYYMMHHThhmmssmm: Indica que todos los elementos de un array de strings son timestamps. Se representa como All the elements of x are Timestamps. Format: YYYY-MM-DDTHH:MM:SS.mmZ (Miliseconds are optional). Definici´on de nuevos invariantes Los invariantes de Daikon pueden ser unarios (solo involucran a una variable), binarios (involucran a dos variables) o ternarios (involucran a tres variables). A˜nadir un nuevo invariante a Daikon requiere escribir una clase Java. Al iniciar su ejecuci´on, Daikon genera instancias de todos los posibles invariantes que pueden generarse. Si durante el an´alisis del DtraceFile, una muestra de un program point contradice a un invariante, este es destruido. Al final de la ejecuci´on, se calcula la fiabilidad (confidence) de cada invariante restante. Si esta m´etrica supera un m´ınimo, el invariante es reportado al usuario. De esta forma, para que un invariante sea reportado deben cumplirse dos condiciones: (1) El invariante se cumple para todas las instancias del program point en el DtraceFile (i.e., no existe ning´un contraejemplo) y (2) la fiabilidad del invariante supera un valor m´ınimo. A continuaci´on, se muestra un ejemplo de un invariante definido en este trabajo (IsUrl) para ejemplificar la estructura que debe seguir un invariante. A la hora de definir la clase que define al invariante, esta debe extender a una subclase de Invariant. En concreto, el invariante IsUrl extiende a SingleString (l´ınea 1 del Extracto 4.13), la clase usada para definir invariantes unarios (aquellos que solo involucran a una variable) para variables de tipo string. Es necesario implementar un constructor y un m´etodo est´atico get proto (Extracto 4.13). El m´etodo get proto se utiliza para instanciar el invariante dentro de un program point (PptSlice es la clase que contiene informaci´on sobre el program point). 1public class IsUrl extends SingleString { 2 3private IsUrl(PptSlice ppt){ super(ppt); } 4 5private @Prototype IsUrl() { super(); } 6 7private static @Prototype IsUrl proto = new @Prototype IsUrl(); 8 9// Returns the prototype invariant 10 public static @Prototype IsUrl get_proto() { return proto; } Extracto de c´odigo 4.13: Constructor y m´etodo get proto Para cada instancia de un program point del DtraceFile, Daikon comprueba que el invariante sigue cumpli´endose, utilizando para ello los m´etodos add modified y check modified, que devuelven un InvariantStatus, cuyo valor puede ser NO CHANGE 31 (el invariante sigue consider´andose v´alido), FALSIFIED (El invariante es destruido) y WEAKENED (la condici´on del invariante es debilitada). check modified recibe una muestra y devuelve si esta es consistente con el invariante. En el caso de IsUrl, el invariante sigue consider´andose v´alido si el valor de la variable sigue una expresi´on regular que represente una URL, siendo destruido en caso contrario. add modified cambia el estado del invariante si es necesario. Si el invariante no tiene un estado (como es el caso de IsUrl), este m´etodo simplemente llama a check modified. Se dice que un invariante tiene estado si este almacena alg´un tipo informaci´on adicional. Por ejemplo, el invariante OneOf indica que una variable solo puede tener un conjunto de valores claramente definidos (el tama˜no m´aximo de dicho conjunto es configurable), se representa como x one of {c1, c2, c3}. El estado de este invariante almacena dicho conjunto de valores de manera que, si durante la ejecuci´on de Daikon se detecta un valor adicional, se llama al m´etodo add modified, que modifica el estado a˜nadiendo dicho valor al conjunto e indicando que el estado del invariante se ha debilitado (i.e., devuelve el InvariantStatus WEAKENED). 1public InvariantStatus check_modified(String v, int count) { 2Pattern pattern = Pattern.compile(urlRegex); 3Matcher matcher = pattern.matcher(v); 4 5if (matcher.matches()) { 6return InvariantStatus.NO_CHANGE; 7} 8return InvariantStatus.FALSIFIED; 9} 10 11 public InvariantStatus add_modified(String v, int count) { return check_modified(v, count); } Extracto de c´odigo 4.14: M´etodos check modified y add modified El m´etodo format using devuelve un string con la representaci´on del invariante que ser´a reportada al usuario (Extracto de c´odigo 4.15). 1public String format_using(@GuardSatisfied IsUrl this, OutputFormat format) { 2return var().name() + " is Url"; 3} Extracto de c´odigo 4.15: M´etodo format using El m´etodo isSameFormula (Extracto 4.16) devuelve true si dos invariantes representan la misma f´ormula matem´atica, sin tener en cuenta contexto como los nombre de las variables, el valor de la confidencialidad o el n´umero de muestras. En el caso de un invariante unario como IsUrl, basta con comprobar que los dos invariantes pertenecen a la misma clase. 1public boolean isSameFormula(Invariant other) { 2if (other instanceof IsUrl) { 3return true; 4} 5return false; 6} Extracto de c´odigo 4.16: M´etodo isSameFormula 32 Finalmente, el m´etodo computeConfidence calcula la fiabilidad de un invariante. El valor de la fiabilidad de un invariante puede encontrarse entre 0 y 1, y debe alcanzar un m´ınimo (0,99 por defecto) para ser reportado al usuario. Por ejemplo, el m´etodo del Extracto 4.17 asume que cada variable tiene una posibilidad del 10 % de ser una URL por casualidad. De esta forma, un conjunto de n valores tiene una posibilidad de (0,1)nde que todos sean URLs por casualidad. 1protected double computeConfidence() { 2return 1 - Math.pow(.1, ppt.num_samples()); 3} Extracto de c´odigo 4.17: M´etodo computeConfidence 4.2.2. Supresores de invariantes Daikon permite definir supresores para evitar reportar informaci´on redundante y hacer la detecci´on de invariantes m´as eficiente. Si un invariante A implica un invariante B, B no es instanciado ni revisado mientras A siga cumpli´endose. Un invariante supresor (definido mediante la clase NISuppressor) pertenece a un conjunto de invariantes (NISuppression) que implican (y por lo tanto suprimen) a un invariante suprimido (definido mediante la clase NISuppressee). El conjunto de todos los invariantes supresores que suprimen a un invariante suprimido se almacenan en la clase NISuppressionSet. Dentro de los invariantes definidos para este trabajo, algunos de los invariantes relacionados con fechas (IsDateDDMMYYYY,IsDateMMDDYYYY eIsDateYYYYMMDD) tienen una longitud fija, por lo que suprimen al invariante FixedLengthString. Para representar esto, definimos un m´etodo get ni suppressions() (Extracto 4.18) dentro de la clase del invariante a suprimir (en este caso, FixedLengthString). Dentro de dicho m´etodo, definimos los supresores, que son devueltos como un conjunto. 1public @NonNull NISuppressionSet get_ni_suppressions() { 2if (suppressions == null) { 3 4NISuppressee suppressee = new NISuppressee(FixedLengthString.class, 1); 5 6// suppressor definitions (used in suppressions below) 7NISuppressor isDateMMDDYYYY = new NISuppressor(0, IsDateMMDDYYYY.class); 8NISuppressor isDateDDMMYYYY = new NISuppressor(0, IsDateDDMMYYYY.class); 9NISuppressor isDateYYYYMMDD = new NISuppressor(0, IsDateYYYYMMDD.class); 10 11 12 suppressions = new NISuppressionSet( 13 new NISuppression[]{ 14 15 new NISuppression(isDateMMDDYYYY, suppressee), 16 new NISuppression(isDateDDMMYYYY, suppressee), 17 new NISuppression(isDateYYYYMMDD, suppressee), 18 19 }); 20 } 21 return suppressions; 22 } Extracto de c´odigo 4.18: M´etodo get ni suppressions 33 De esta forma, si el valor de una variable es siempre una fecha en uno de los formatos que constituyen el conjunto de supresores, Daikon no calcular´a (ni reportar´a) el estado de FixedLengthString, a menos que en un program point futuro el valor de la variable deje de seguir el formato de la fecha, pero siga manteniendo una longitud fija. En ese caso, el invariante supresor ser´a destruido y el invariante suprimido dejar´a de considerarse como tal, report´andose al usuario si no se incumple en ning´un program point posterior del DtraceFile. 4.2.3. Supresi´on de variables derivadas Una variable derivada es una expresi´on que no aparece directamente en el programa instrumentalizado, pero que Daikon utiliza como variable para la detecci´on de invariantes. Por ejemplo, para las variables de tipo array, Daikon genera una variable derivada cuyo valor sea su tama˜no, permitiendo detectar invariantes como input.limit >= size(return.items). Sin embargo, existen variables derivadas que no aportan ning´un tipo de informaci´on relevante, este es el caso de la variable derivada orig(), que indica el valor de una variable en el program point de entrada y se utiliza para realizar comparaciones con el valor de esa misma variable en el program point de salida. En el contexto de las pruebas de caja negra de APIs RESTful, el valor de los par´ametros de entrada no cambia en ning´un momento, por lo que Daikon detectar´ıa ´unicamente invariantes de igualdad entre el valor del par´ametro en la entrada y en la salida (input.limit == orig(input.limit)) que siempre se cumplir´an. Por este motivo, esta variable derivada ha sido deshabilitada. 34 5.3.2. Impacto del tama˜no del conjunto de pruebas en la calidad de los or´aculos generados La gr´afica de la Figura 5.2 muestra la evoluci´on del valor de la precisi´on obtenida por la propuesta (Columna “P( %)” de la Tabla 5.2) a medida que incrementa el n´umero de casos de prueba utilizados para la detecci´on de invariantes. Puede observarse que no existe una variaci´on significativa para 6 de las 8 operaciones utilizadas para la evaluaci´on. Las ´unicas operaciones en las que se produce un cambio significativo de la precisi´on son la operaci´on “Search businesses” de la API de Yelp y la operaci´on “By ID or Title” de OMDb. Figura 5.2: Evoluci´on de la precisi´on de la propuesta En el caso de la API de Yelp, el aumento del n´umero de casos de prueba ha permitido la eliminaci´on de invariantes que establec´ıan comparaciones aritm´eticas que no necesariamente deben cumplirse, as´ı como invariantes que especificaban que un valor de la respuesta solo ten´ıa un valor concreto. Por ejemplo, en los 50 primeros casos de prueba se hab´ıa cumplido que el n´umero de rese˜nas de todos los establecimientos era mayor que el n´umero de tipos de transacciones que soportaba (return.review count >size(return.transactions[])). Adem´as, para las primeras 500 llamadas se cumpli´o que todos los establecimientos estaban abiertos (return.is closed == false). Al generar invariantes potenciales a partir de un conjunto de 50 pruebas de la operaci´on “By ID or Title” de la API de OMDb, la mayor´ıa de los falsos positivos establecen que ciertos campos deben tener un conjunto de valores muy concreto (e.g., return.Episode one of {"14", "8"}). Todos estos falsos positivos, exceptuando uno, no est´an presentes al procesar el conjunto de 100 llamadas, y desaparecen por completo al procesar el conjunto de 500. La raz´on por la que se produce un decremento en la precisi´on al alcanzar las 1000 llamadas se debe a que existe una ´unica llamada en 41 todo el conjunto de pruebas que devuelva un valor para el campo Production de la respuesta, lo que resulta en la aparici´on del invariante return.Production == "Crappy World Films". En vista de estos resultados, podemos responder a RQ2 como sigue: RQ2: El incremento del n´umero de casos de prueba utilizados para la detecci´on de invariantes no supone una variaci´on significativa en la precisi´on obtenida. Solo se ha producido una variaci´on apreciable en 2 de las 8 operaciones utilizadas para realizar la evaluaci´on. 5.3.3. Capacidad de detecci´on de errores de la propuesta Los or´aculos generados pueden implementarse como assertions que permitan detectar errores en futuras respuestas de la API. No obstante, si la API muestra un comportamiento err´oneo, existe la posibilidad de que esto se vea reflejado en alguno de los invariantes detectados, lo que permite detectar bugs en la implementaci´on o inconsistencias en la documentaci´on al interpretarlos. Las columnas 4, 7, 10 y 13 de la Tabla 5.2 muestran el n´umero de invariantes por operaci´on que revelan la existencia de un bug o una inconsistencia. En total, se han detectado 12 invariantes de este tipo en 3 de las 6 APIs usadas para la evaluaci´on. Dichos invariantes han permitido identificar 6 bugs replicables en 4 de las 8 operaciones. N´otese que, exceptuando el caso de un invariante de este tipo en la API de Amadeus para cuya detecci´on ha sido necesario emplear 100 casos de prueba, para detectar el resto de bugs solo se ha requerido de 50 casos de prueba. En los siguientes apartados se describen los errores detectados en cada API. Amadeus Hotel – Habitaciones sin camas La operaci´on “Find hotel offers” de la API de Amadeus Hotel permite buscar ofertas de habitaciones de hoteles a partir de filtros como el id del hotel o la fecha de entrada y salida, entre otros. Una de las propiedades de la respuesta contiene las caracter´ısticas de la habitaci´on a reservar, entre las que se encuentra su n´umero de camas. Seg´un la especificaci´on OAS oficial de la API (Extracto de c´odigo 5.1), el n´umero de camas de una habitaci´on debe oscilar entre 1 y 9. 1HotelProduct_EstimatedRoomType: 2type: object 3properties: 4category: 5... 6beds: 7description:’Number of beds in the room (1-9)’ 8type: integer 9bedType: 10 ... Extracto de c´odigo 5.1: Fragmento de la especificaci´on OAS de Amadeus Hotel. 42 Sin embargo, uno de los invariantes detectados especifica que el n´umero de camas de una habitaci´on debe ser mayor o igual que 0 (return.room.typeEstimated.beds >= 0), lo que revela que algunas de las habitaciones devueltas por los casos de prueba contienen 0 camas. Al analizar el conjunto de respuestas, se han detectado un total de 55 ofertas de hoteles en las que la habitaci´on ten´ıa 0 camas. A pesar de esto, los datos de la habitaci´on conten´ıan informaci´on sobre el tipo de cama (como se muestra en el Extracto de c´odigo 5.2). 1"room":{ 2"type":"A0K", 3"typeEstimated":{ 4"category":"SUITE", 5"beds":0, 6"bedType":"KING" 7} 8} Extracto de c´odigo 5.2: Respuesta con 0 camas. GitHub – Inconsistencia en la documentaci´on La operaci´on “List organization repositories” de la API de GitHub permite, dado el identificador de una organizaci´on de GitHub, listar todos sus repositorios. De acuerdo con la documentaci´on oficial (tanto la documentaci´on web como la especificaci´on OAS), uno de los campos de cada repositorio devuelto es un objeto que contiene las propiedades del repositorio plantilla (conocido como template repository) usado para la creaci´on de cada uno de los repositorios (en caso de que no se haya usado un repositorio plantilla, este campo ser´a nulo). A pesar de que algunos de los repositorios de las organizaciones usadas para la creaci´on del conjunto de pruebas utilizaban repositorios plantillas, 9 de los invariantes detectados por la propuesta especifican que todas las propiedades del campo template repository de la respuesta son nulos (e.g., return.template repository==null). Este invariante ha permitido detectar que la propiedad template repository nunca est´a presente en la respuesta de la API. Este comportamiento ya ha sido notificado a los proveedores de la API, que han confirmado que se trata de una inconsistencia en la documentaci´on y han creado una issue interna para actualizarla (Figura 5.3). El siguiente v´ıdeo muestra la replicaci´on de esta inconsistencia [11]. OMDb – Valor de par´ametro no especificado en la documentaci´on El par´ametro type de las operaciones “By ID or Title” y “By Search” de la API de OMDb puede utilizarse para filtrar los resultados devueltos por la API a un tipo concreto de producci´on, que puede ser “movie”, “series” o “episode”, tal y como se especifica en la documentaci´on oficial (Figura 5.4). Sin embargo, uno de los invariantes detectados en la operaci´on “By Search” muestra que este filtrado por tipo no funciona de la forma especificada en la documentaci´on. 43 Figura 5.3: Confirmaci´on del error por parte de los proveedores de la API de GitHub. Figura 5.4: Par´ametro type de la API de OMDb. Dicho invariante es return.Type one of {"game", "movie", "series"}. Este invariante ha permitido identificar 2 bugs e inconsistencias en esta operaci´on. La primera de estas inconsistencias es que es posible establecer el valor del par´ametro type como “game”, valor no especificado en la documentaci´on (en el siguiente v´ıdeo puede verse el uso de este valor para el par´ametro type [15]). El segundo bug consiste en que no es posible filtrar los resultados por episodio (de serlo, el invariante anterior incluir´ıa “episode” como uno de los valores). Se ha detectado que tampoco es posible realizar este filtrado en la operaci´on “By ID or Title”, por lo que se han detectado un total de 3 bugs en esta API a partir de un solo invariante. La replicaci´on de este bug en ambas operaciones puede observarse en el siguiente v´ıdeo [14]. OMDb – Puntuaciones inconsistentes El campo imdbRating de la respuesta de la operaci´on “By ID or Title” de OMDb contiene la nota num´erica, del 0 al 10, y con una sola cifra decimal, que ha recibido una producci´on. Uno de los invariantes detectados especifica que la longitud de este campo siempre es 3 (LENGTH(return.imdbRating)==3), lo que revela ninguna de las producciones tiene una puntuaci´on de 10.0. Sin embargo, algunas de las predicciones del conjunto de datos s´ı tienen una puntuaci´on de 10.0 en la p´agina oficial de IMDb, pero la API devuelve un 9.9, lo que revela una inconsistencia en los datos de la API. El siguiente v´ıdeo muestra la replicaci´on de esta inconsistencia [16]. En vista de estos resultados, podemos responder a RQ3 de la siguiente forma: 44 RQ3: La propuesta es efectiva detectando errores a partir de un conjunto de casos de prueba. En total, se han identificado un total de 6 bugs replicables en 4 operaciones pertenecientes a 3 APIs comerciales con millones de usuarios. 45 6. Amenazas a la validez En este cap´ıtulo se discuten las posibles amenazas a la validez, tanto internas como externas, que puedan haber influido en este trabajo, y como estas han sido mitigadas. 6.1. Validez interna ¿Existen factores que puedan haber afectado a los resultados de la evaluaci´on? Para la experimentaci´on, se ha utilizado la especificaci´on OAS de cada una de las APIs que componen el conjunto de evaluaci´on. Cuando ha sido posible, se han utilizado especificaciones que estuviesen disponibles p´ublicamente. Sin embargo, en el caso de OMDb y Yelp, ha sido necesario crear esta especificaci´on manualmente a partir de la documentaci´on web. Por lo tanto, existe el riesgo de que alguna estas especificaciones contenga errores que las diferencie de la documentaci´on web. Para mitigar esta posible amenaza, cada una de estas especificaciones ha sido revisada exhaustivamente. Adem´as, RESTest comprueba autom´aticamente que no exista ninguna inconsistencia entre la especificaci´on y la respuesta devuelta por la API para cada caso de prueba, algo que no ha ocurrido en ninguna de las pruebas que conforman nuestro conjunto de datos. La variedad de las respuestas devueltas por las APIs depende en gran medida de los valores establecidos para cada uno de los par´ametros de entrada, por lo que un conjunto de valores de entrada muy peque˜no o poco variado podr´ıa resultar en sesgos. Para ejercitar la l´ogica de la API lo m´aximo posible, se ha buscado seleccionar un conjunto de valores de entrada amplio y variado para cada uno de los par´ametros de entrada. Por ejemplo, en el caso de la API de Amadeus, se han aportado un total de 262 c´odigos de hoteles distintos, pertenecientes a distintos pa´ıses y continentes. La clasificaci´on de cada uno de los invariantes detectados como verdaderos positivos o falsos positivos puede verse afectada por sesgos humanos o errores a la hora de interpretar el dominio de la API. Para mitigar esta amenaza, la documentaci´on (tanto la especificaci´on OAS como la versi´on web) de cada API ha sido revisada exhaustivamente antes de clasificar cada uno de los invariantes, contactando a los desarrolladores cuando fuese necesario aclarar posibles ambig¨uedades. Por ´ultimo, la existencia de errores en el instrumenter implementado podr´ıa comprometer la validez de los resultados. Para mitigar esto, el sistema ha sido probado de forma exhaustiva, con un total de 61 tests unitarios que abarcan toda la funcionalidad del sistema. 46 6.2. Validez externa ¿Hasta qu´e punto se pueden generalizar los resultados de esta investigaci´on? La propuesta presentada en este trabajo ha sido evaluada con un subconjunto de 8 operaciones de 6 APIs distintas, por lo que las conclusiones obtenidas podr´ıan no generalizarse m´as all´a de estas operaciones. Para mitigar esta posible amenaza, se ha buscado evaluar la propuesta con un conjunto de APIs pertenecientes a dominios diferentes y con un n´umero variado de operaciones y par´ametros, que ya han sido utilizadas previamente en evaluaciones de otras propuestas para la generaci´on autom´atica de pruebas en APIs RESTful. Por otra parte, se ha extendido Daikon a˜nadiendo un total de 22 nuevos invariantes. El uso de estos tipos de invariantes ha permitido generar una gran cantidad de or´aculos en el conjunto de APIs usados para la evaluaci´on, pero existe la posibilidad de que estos resultados no se puedan generalizar. Sin embargo, cabe destacar que estos invariantes han sido generados a partir de un an´alisis del comportamiento de un dataset de 48 APIs creado por el autor en una publicaci´on anterior [34]. Igualmente, es importante se˜nalar que el conjunto de tipos de invariantes no pretende ser completo y que cabe la posibilidad de a˜nadir nuevos tipos de invariantes en el futuro. 47 7. Conclusiones y trabajo futuro En este trabajo se ha presentado una propuesta para la generaci´on autom´atica de or´aculos de prueba para APIs RESTful. Dichos or´aculos se obtienen mediante la instrumentalizaci´on de la especificaci´on OAS de la API y de un conjunto de casos de prueba para los que se conocen ´unicamente sus entradas y salidas. Al no requerir de acceso al c´odigo fuente de la API, la propuesta opera en un contexto de caja negra, lo que la hace aplicable a cualquier API RESTful, independientemente de la tecnolog´ıa utilizada para su implementaci´on. La evaluaci´on realizada sobre un conjunto de 8 operaciones de 6 APIs comerciales muestra la capacidad de la propuesta para generar or´aculos v´alidos a partir de un conjunto de casos de prueba, llegando a obtener una precisi´on del 100 % en algunos casos. Adem´as, la aplicaci´on de la propuesta ha permitido detectar un total de 6 bugs replicables en 3 APIs comerciales con millones de usuarios (Amadeus, GitHub y OMDb). A continuaci´on, se presentan las principales l´ıneas de trabajo futuro identificadas: Generaci´on autom´atica de assertions: Actualmente, los invariantes devueltos por la propuesta se especifican en un formato que, aunque es f´acil de interpretar por un humano, no puede utilizarse directamente para generar assertions que puedan insertarse directamente en un c´odigo escrito en un lenguaje de programaci´on espec´ıfico. Ahora que se ha comprobado la viabilidad de la propuesta para generar or´aculos de prueba y detectar errores, la generaci´on autom´atica de estas assertions es el primer paso a realizar en la extensi´on de este trabajo. Despliegue como API: Esta mejora permitir´ıa que la propuesta fuese accesible para cualquier usuario con acceso a internet, omitiendo la necesidad de instalar y configurar todas las herramientas necesarias para su uso. Extracci´on y validaci´on de nuevos valores para los par´ametros de entrada: El autor de esta propuesta ha dise˜nado e implementado un m´etodo para la generaci´on autom´atica de valores de entrada a partir de la especificaci´on OAS de una API, que ya ha sido publicado [34]. Los invariantes detectados podr´ıan usarse para mejorar dicha herramienta mediante la validaci´on autom´atica de los valores generados (por ejemplo, detectando que el uso de un valor siempre resulta en un mensaje de error) o para obtener nuevos valores de entrada a partir de las respuestas de la API. Esto ´ultimo podr´ıa lograrse explotando la informaci´on aportada por aquellos invariantes que revelan que el valor de un par´ametro de la entrada siempre es igual que un campo de la salida. 48 A. Bibliograf´ıa [1] APIFuzzer website. https://github.com/KissPeter/APIFuzzer. Accessed June 2022. [2] Evomaster website. https://github.com/EMResearch/EvoMaster. Accessed June 2022. [3] OMDb API. http://www.omdbapi.com. Accessed May 2022. [4] Amadeus Hotel Shopping API. https://developers.amadeus.com/ self-service/category/hotel/api-doc/hotel-search/api-reference. Accessed May 2022. [5] bBoxrt website. https://git.dei.uc.pt/cnl/bBOXRT. Accessed June 2022. [6] BeSoccer API. https://www.besoccer.com/api/. Accessed May 2022. [7] Daikon developer manual. https://plse.cs.washington.edu/daikon/download/ doc/developer.html, . Accessed May 2022. [8] Daikon user manual. https://plse.cs.washington.edu/daikon/download/doc/ daikon.html, . Accessed June 2022. [9] DeepL API. https://www.deepl.com/docs-api/. Accessed May 2022. [10] Dredd website. https://github.com/apiaryio/dredd. Accessed June 2022. [11] Bug GitHub API - Repository template. https://www.youtube.com/watch?v= sdA9Gc4usSg&ab channel=JuanCarlosAlonsoValenzuela, . Accessed June 2022. [12] GitHub API - List organization repositories. https://docs.github.com/en/ rest/repos/repos, . Accessed June 2022. [13] HTTP status codes. https://www.restapitutorial.com/httpstatuscodes. html. Accessed June 2022. [14] OMDb API - Cannot filter by episode. https://www.youtube.com/watch?v= 5cXIHDDwpNE&ab channel=JuanCarlosAlonsoValenzuela, . Accessed June 2022. [15] OMDb API - type game. https://www.youtube.com/watch?v=p NMY4bBGFc&ab channel=JuanCarlosAlonsoValenzuela, . Accessed June 2022. [16] OMBb API - Inconsistent IMDbRating. https://www.youtube.com/watch?v= K6MEHhZOJGs&ab channel=JuanCarlosAlonsoValenzuela, . Accessed June 2022. [17] OpenAPI Specification. https://www.openapis.org, . Accessed May 2022. [18] Openroute API. https://openrouteservice.org/dev/#/api-docs, . Accessed May 2022. 49 [19] Programmable Web API directory. https://www.programmableweb.com. Accessed May 2022. [20] RapidAPI API directory. https://rapidapi.com/marketplace. Accessed May 2022. [21] Replication package. https://doi.org/10.5281/zenodo.6687916. Accessed June 2022. [22] RESTest website. https://github.com/isa-group/RESTest, . Accessed June 2022. [23] Restler website. https://github.com/microsoft/restler-fuzzer, . Accessed June 2022. [24] RestTestGen website. https://github.com/SeUniVr/RestTestGen, . Accessed June 2022. [25] SchemaThesis website. https://github.com/schemathesis/schemathesis. Accessed June 2022. [26] Spotify API - Create Playlist. https://developer.spotify.com/console/ post-playlists/, . Accessed June 2022. [27] Spotify API - Get Album Tracks. https://developer.spotify.com/console/ get-album-tracks/, . Accessed June 2022. [28] Dredd website. https://github.com/Cornutum/tcases/tree/master/ tcases-openapi. Accessed June 2022. [29] Twitter API. https://developer.twitter.com/en/docs. Accessed May 2022. [30] Yelp Fusion API - Businesses Search. https://www.yelp.com/developers/ documentation/v3/business search. Accessed June 2022. [31] YouTube API - List Videos. https://developers.google.com/youtube/v3/ docs/videos/list. Accessed June 2022. [32] DAIKON front-ends. https://plse.cs.washington.edu/daikon/download/doc/ daikon.html#Front-ends- 0028instrumentation 0029, 2022. Accessed June 2022. [33] Afsoon Afzal, Claire Le Goues, and Christopher Steven Timperley. Mithra: Anomaly detection as an oracle for cyberphysical systems. IEEE Transactions on Software Engineering, pages 1–1, 2021. doi: 10.1109/TSE.2021.3120680. [34] Juan C. Alonso, Alberto Martin-Lopez, Sergio Segura, Jose Maria Garcia, and Antonio Ruiz-Cortes. ARTE: Automated Generation of Realistic Test Inputs for Web APIs. IEEE Transactions on Software Engineering, 2022. doi: 10.1109/TSE. 2022.3150618. 50