scieee AI-readable full text Open interactive document viewer

Visualización del rendimiento en equipos de desarrollo Software a través de sistemas de control de versiones Git

Aniceto Caba, María de la Luz

Abstract

La transformación digital está llevando a un crecimiento exponencial en la creación de empleo de desarrollo Software. Muchas empresas son las que, adaptándose al marco de las nuevas tecnologías, requieren de departamentos técnicos o servicios software externos. Nos encontramos por ello ante un interés por aplicar metodologías y herramientas que ayuden a los gestores de proyectos software a optimizar el proceso de desarrollo de forma continua. La calidad de este proceso debe ser medible y fiable. En este trabajo se presenta el estudio y desarrollo de una aplicación web que permite conocer de forma visual y sencilla la calidad e impacto de las tareas realizadas en un equipo de desarrollo Software. De modo que los datos representados puedan ayudar a un equipo de desarrolladores a tomar mejores decisiones, detectar problemas, fomentar el uso de buenas prácticas…

Full text

Equation Chapter 1 Section 1 Trabajo Fin de Grado en Ingeniería de las Tecnologías de Telecomunicación Visualización del rendimiento en equipos de desarrollo Software a través de sistemas de control de versiones Git. Autor: María Luz Aniceto Caba. Tutor: Isabel Román Martínez. Dpto. de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2020 iii iii Trabajo Fin de Grado en Ingeniería de las Tecnologías de Telecomunicación Visualización del rendimiento en equipos de desarrollo Software a través de sistemas de control de versiones Git. Autor: María Luz Aniceto Caba Tutor: Isabel Román Martínez Profesor colaborador Dpto. de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2020 v v Trabajo Fin de Grado: Visualización del rendimiento en equipos de desarrollo Software a través de sistemas de control de versiones Git. Autor: María Luz Aniceto Caba Tutor: Isabel Román Martínez El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: Sevilla, 2020 El secretario del Tribunal vii vii A mi familia. ix ix Agradecimientos No puedo dejar este trabajo sin agradecer la infinita paciencia de todos los que me rodean: gracias por apoyarme y animarme cada vez que las fuerzas flaqueaban entre intento e intento. Gracias a mis padres que han estado apoyándome siempre. Gracias a Jesús, Ale, Luis, Franxi y Pablo con los que he emprendido un camino de aventuras. A mi compañera de batallas en la escuela. A mí tutora, que siempre creyó en esta idea, que ha cuidado cada revisión y me ha animado en cada una. Y al más paciente de todos Jesús, que tu curiosidad no deje de ilusionarte siempre. Gracias. Que a toda verdad demos la bienvenida. María Luz Aniceto Caba Sevilla, 2020 ÍNDICE DE TABLAS Tabla 1: Objetivos OBJ-01 - Desarrollar un algoritmo que estime el valor de una tarea 4 Tabla 2: Objetivos OBJ-02 – Visualización General 4 Tabla 3: Objetivo OBJ-03 –Visualización Individual 5 Tabla 4: Objetivo OBJ-04 – Visualización del tiempo en terminar tareas 5 Tabla 5: Objetivo OBJ-05 – Visualización de ficheros más modificados 5 Tabla 6: Valores posibles de la variable V y su descripción 17 Tabla 7: Colores y significado de la visualización principal 18 Tabla 8: Actores del sistema: ACT_01 Usuario 36 Tabla 9: Actores del sistema: ACT_02 API de GitHub 37 Tabla 10: Actores del sistema: ACT_03 Caché 37 Tabla 11: Actores del sistema: ACT_04 Firebase 37 Tabla 12: Requisitos generales RG_001 - Autentificación/Inicio de Sesión 37 Tabla 13: Requisitos generales RG_002 -Buscar entre sus repositorios 38 Tabla 14: Requisitos generales RG_003 – Visualizaciones 38 Tabla 15: Requisitos generales RG_004 - Navegar entre visualizaciones 39 Tabla 16: Requisitos generales RG_005 - Cerrar sesión 39 Tabla 17: Requisitos de información: RF.INF_001 - Usuario 40 Tabla 18: Requisitos de información: RF.INF_002 - Repositorios 40 Tabla 19: Requisitos de conducta: RFC_001 - Mostrar y gestionar errores 41 Tabla 20: Requisitos de conducta: RFC_002 - Visualización de red de nodos simple 41 Tabla 21: Requisitos de conducta: RFC_003 - Gráfico de ficheros más modificados 42 Tabla 22: Requisitos de conducta: RFC_004 - Gráfico estimación duración de las tareas 42 Tabla 23: Requisitos de conducta: RFC_005 - Pantalla de carga 43 Tabla 24: Requisitos de conducta: RFC_006 - Leyendas 43 Tabla 25: Requisitos de conducta: RFC_007 - Desplegar información del gráfico de nodos. 43 Tabla 26: Requisitos de Fiabilidad: RNF_001 - Disponibilidad de los datos 44 Tabla 27: Requisitos de seguridad: RNFS_001 - Autentificación y Autorización 44 Tabla 28: Requisitos de seguridad: RNFS_002 - Borrado de datos al cerrar sesión 45 Tabla 29: Requisitos de eficiencia: RNFE_001 - Tiempo de respuesta menor en posteriores peticiones 45 xvii xvii ÍNDICE DE FIGURAS Figura 1: Modelo de proceso software en espiral 2 Figura 2: Mapa de calor de las contribuciones realizadas en GitHub 3 Figura 3: Ranking de causas de deuda técnica más comunes 3 Figura 4: Esquema de control de versiones git. 7 Figura 5: Representación gráfica de ramas (líneas) y commits (círculos) 7 Figura 6: Crecimiento del uso de Pull Request en GitHub durante los años 2011 al 2016 [9] 8 Figura 7: Representación gráfica de la creación de una nueva rama en git. 9 Figura 8: Representación gráfica del comando push en git 9 Figura 9: Interfaz gráfica de GitHub para la creación de un Pull Request 10 Figura 10: Representación gráfica del comando pull de git 10 Figura 11: Resumen del proceso de trabajo basado en Pull Request [10] 11 Figura 12: Factores que influyen en los tiempos de revisiones, en primer lugar, líneas modificadas. 12 Figura 13: Motivación de los desarrolladores para hacer revisiones. 13 Figura 14: Interfaz gráfica de GitHub para dejar comentarios en líneas de código 14 Figura 15: Ejemplo de visualización principal 15 Figura 16: Densidad de fallos encontrados conforme aumenta LoC(Líneas de código) 16 Figura 17: visualización de ejemplo de un desarrollador. 18 Figura 18: Varios desarrolladores y sus contribuciones a un proyecto de código abierto 19 Figura 19: Visualización que muestra cuanto se ha tardado en realizar tres pull requests 19 Figura 20: Visualización de ficheros más modificados. 20 Figura 21: Diagrama jerárquico del flujo de las propiedades a través de los componentes en React. 23 Figura 22: Paso de propiedades por componentes usando Redux vs sin esta herramienta. 24 Figura 23: React + Redux esquema resumen 26 Figura 24: Modelo API REST de GitHub 27 Figura 25: Funcionamiento del servidor que ofrece una API GraphQL 27 Figura 26: Modelo GraphQL de GitHub 28 Figura 27: Ejemplo de consulta y resultado en GraphQL a la API de GitHub 28 Figura 28: Ventana emergente de autorización para Github 30 Figura 29: Interfaz de React Developers Tool en funcionamiento 31 Figura 30: DevTools de Google Chrome. Ejemplo de un punto de parada en el código. 31 Figura 31: Mensaje que aparece en la interfaz gráfica de GitHub integrado con CircleCI cuando los tests no son correctos. 32 Figura 32: Mensaje integrado en un Pull Request de la integración de Vercel con GitHub 33 Figura 33: Interfaz gráfica de Github integrado con Vercel. 34 Figure 34: Herramienta Vercel Logs del despliegue de una rama en detalle. 35 Figura 35: Interfaz gráfica del servicio Vercel, lista de despliegues. 35 Figura 36: captura del directorio src/ de la aplicación 53 Figura 37: Salida del comando npm run tests 60 Figure 38: Interfaz de GitHub para genera un token 61 Figura 39: Resultado de las pruebas unitarias para la obtención de datos del api 65 Figura 40: Proyectos analizados desde mayo del 2019 a mayo de 2020 70 xix xix ÍNDICE DE DIAGRAMAS Diagrama 1: Diagrama de paquetes del sistema y relación con servicios externos. 46 Diagrama 2: Diagrama general de componentes 47 Diagrama 3: Diagrama de objetos del estado de Redux. 48 Diagrama 4: Diagrama de objeto del estado de la aplicación antes de iniciar sesión. 49 Diagrama 5: Diagrama de Secuencia: Inicio de Sesión 49 Diagrama 6: Diagrama de secuencia Cierre de Sesión 50 Diagrama 7: Diagrama de actividad de la aplicación desde el inicio de sesión al análisis un proyecto. 51 Diagrama 8: Diagrama de actividad Navegar entre visualizaciones 52 ÍNDICE DE CÓDIGOS Código 1: Ejemplo de declaración del Store inicial. 25 Código 2: Ejemplo del envío de la acción añadir_nombre al Store con el parámetro nombre. 25 Código 3: Ejemplo de función reductor que recibe como parámetros el estado y la acción lanzada. 26 Código 4: Definición de una función suma simple. 29 Código 5: Importación del archivo donde se encuentra la función suma 29 Código 6: Declaración del test para la función suma 29 Código 7: Resultado correcto de la ejecución del test para la función suma. 29 Código 8: Fichero de configuración de CircleCI 33 Código 9: Fichero de configuración del servicio Vercel. 34 Código 10: Fichero script para los tests parte I 61 Código 11: Fichero script para los tests parte II 61 Código 12: Fichero script para los tests parte III 62 Código 13: Test que Obtiene los datos de la API correctamente de un proyecto privado 63 Código 14: Código del test para un proyecto que no obtiene datos 64 Código 15: Test para un proyecto que no tiene pull requests que analizar 66 Código 16: Código del script para la prueba de interfaz 67 xxi xxi ÍNDICE DE CAPTURAS DE LA APLICACIÓN Captura 1: Interfaz de Inicio de sesión 54 Captura 2: Información del usuario y botón de cerrar sesión 54 Captura 3: Interfaz mostrada cuando no hay ningún repositorio disponible 55 Captura 4: Desplegable para seleccionar la visualización 55 Captura 5: Aplicación cargando los datos 56 Captura 6: Interfaz con la visualización principal 56 Captura 7: Componente que muestra en detalle cada una de las tareas 57 Captura 8: Interfaz con la visualización de tareas 57 Captura 9: Interfaz de la visualización de ficheros más modificados 58 Captura 10: Mensaje de error cuando no existe el proyecto que se ha buscado 58 Captura 11: Barra de proyectos cuando uno no se ha podido cargar 58 Índice de Capturas de la aplicación 1 INTRODUCCIÓN a transformación digital está llevando a un crecimiento exponencial en la creación de empleo de desarrollo Software. Muchas empresas son las que, adaptándose al marco de las nuevas tecnologías, requieren de departamentos técnicos o servicios software externos. Nos encontramos por ello ante un interés por aplicar metodologías y herramientas que ayuden a los gestores de proyectos software a optimizar el proceso de desarrollo de forma continua. La calidad de este proceso debe ser medible y fiable. La ingeniería del software [1] estudia las metodologías para el desarrollo y mantenimiento de sistemas software. Los objetivos de este tipo de ingeniería son: • Mejorar el diseño • Promover mayor calidad al desarrollar aplicaciones complejas • Detectar problemas y proponer mejoras • Mejorar la rentabilidad al desarrollar un proyecto. • Mejorar la organización de los equipos de trabajo. Hay distintos modelos del proceso software que proponen diferentes aproximaciones para enfrentar las actividades implicadas en el proceso software con el fin de obtener un producto de calidad, como por ejemplo el modelo de proceso en Espiral: L Lo que no se mide, no se controla, y lo que no se controla, no se puede mejorar. - Peter Drucker- Introducción 2 Figura 1: Modelo de proceso software en espiral En cada una de las fases se pueden usar aplicaciones y herramientas que ayudan a los ingenieros a cumplir los objetivos de manera más fácil. Además de buscar la optimización del producto, la mejora continua del proceso es otro objetivo. Como sin métricas no podemos mejorar, necesitamos conocer datos y medidas del proceso software, para conseguir evaluarlo y mejorarlo. En este proyecto queremos centrarnos en las fases de evaluación, programación de pruebas y desarrollo e intentar medir cómo se está ejecutando este proceso de una manera fiable. Así podremos tener mejor conocimiento sobre todo el flujo de trabajo. 1.1 Motivación La etapa de implementación o desarrollo conlleva una verificación continua de la calidad del software y del propio proceso ¿Cómo podemos conocer cómo está trabajando nuestro equipo y cómo es la calidad de sus tareas? ¿Cómo sabemos lo bien que funciona un equipo? La calidad del proceso debe ser medible, y de forma fiable. Uno de los parámetros que es muy importante para valorar la esta calidad es el rendimiento de los desarrolladores. Pero esto no es algo fácil de evaluar. El trabajo de desarrollo software conlleva la toma muchas decisiones que pueden tener efectos secundarios. Medir, exclusivamente, la cantidad de código que genera un desarrollador de forma numérica sería inapropiado. Una de las métricas que más se valoran en la industria del software es la cantidad de líneas de código añadidas (LoC: Lines of Code) por ingeniero en un día usada, por ejemplo, para la medida del rendimiento en el desarrollo en Facebook [2]. No obstante, esta métrica genera los siguientes problemas: • Se puede deformar fácilmente, un desarrollador puede añadir líneas extras para alcanzar números altos. Básicamente la conocida Ley de Campbell [3] que nos dice “que cuanto más usado sea un indicador cuantitativo para la toma de decisiones más probable será que se corrompa la medida de este indicador”. • El número de líneas escritas no es un parámetro fiable para medir la calidad de una tarea. Se pueden estar añadiendo líneas inútiles, que solo están complicando el código y provocando el alejamiento del principio KISS (En inglés: Keep It Simple, Stupid!). • Copiar y pegar código evita que se reestructure el mismo y nuevamente se introduzca código inútil. Esta métrica sólo es interesante si no la aislamos, es decir, si la ponemos en contexto con otros datos del trabajo de un desarrollador, como veremos más tarde. Esto que ocurre con las líneas de código pasa con otros tipos de métricas, como contar fallos arreglados, tareas completadas, número de revisiones… Todas estas métricas son susceptibles de deformar la toma de decisiones al usarse de forma incorrecta. 3 3 En GitHub se nos muestra un mapa de calor sobre la actividad de una persona, y es una métrica fiable de la cantidad de tareas realizadas, pero no de la calidad de las mismas. La unión de calidad y cantidad nos daría un indicador más cercano a la realidad del trabajo de un desarrollador. Como se explicará más tarde: existen muchos factores importantes que no estamos evaluando. Figura 2: Mapa de calor de las contribuciones realizadas en GitHub No valorar de forma adecuada el desempeño de los integrantes de un equipo y el progreso de un proyecto nos puede llevar a generar deuda técnica. La deuda técnica es entendida en el sector como malas decisiones técnicas, código extremadamente complejo, falta de documentación… Se reconoce, entre los desarrolladores, que podría ser solventado con una buena metodología de trabajo [5]. La deuda técnica [4] se genera cuando se toman decisiones incorrectas, impidiendo en un futuro avanzar por tener que solucionar los defectos introducidos. Normalmente estas decisiones se toman por presión en las fechas de entrega o por no estar siguiendo una adecuada metodología de buenas prácticas. Figura 3: Ranking de causas de deuda técnica más comunes La falta de datos fiables y con ello la deuda técnica, nos lleva a encontrarnos con problemas mayores como: no estar valorando el talento de tu equipo, no llegar a tiempo a fechas de entrega, estar construyendo un software de mala calidad, etc. Todo esto, acaba inexorablemente generando grandes pérdidas a las empresas. Marco Teórico 10 te da a escoger entre las ramas disponibles con las que crear el pull request. Como vemos en la imagen, se nos muestra nueva-rama en la parte superior. Los siguientes pasos son añadir título y descripción de la rama. Figura 9: Interfaz gráfica de GitHub para la creación de un Pull Request 3. Otro desarrollador tendrá que revisar la tarea y comprobar que está correcta. Puede dejar comentarios, sugerencias de cambios, aprobar la tarea… Puede, incluso, hacer commits a la rama nueva-rama ya que con el comando pull puede descargar la rama a su repositorio local y hacer cambios propios. Después sólo tiene que hacer push para incluirlos tanto en la rama remota como en el pull request, ya que la rama está vinculada y sincronizada con el Pull Request. Figura 10: Representación gráfica del comando pull de git 11 11 4. Si todo está correcto la rama se fusiona con la rama remota, si se concluye que no se va a fusionar se cierra el Pull Request. Figura 11: Resumen del proceso de trabajo basado en Pull Request [10] Cabe destacar que al fusionarse la rama del pull request con la rama remota principal se puede hacer de dos formas diferentes: - Squash and merge: todos los commits de la rama nueva se incluyen en la rama remota como un solo commit que tendrá como mensaje descriptivo el título del pull request. - Merge: todos los commits de la rama se añaden a la rama remota. 2.5 Métricas destacables Como bien hemos visto en la introducción, tener sólo datos cuantitativos no nos hace valorar justamente el trabajo de un desarrollador. A través de los datos que nos ofrece la API de GitHub estimaremos el impacto de una tarea o una revisión para poder mostrarlo en un gráfico. Después de haber explicado el modelo pull based destacaremos tres unidades de trabajo básicas que podremos evaluar: pull request, revisión y revisión con commit. Vamos a elegir estas tres unidades de trabajo ya que son los elementos más destacables del método pull-based. A continuación, detallaremos qué tipo de métricas se asocian a cada una de estas unidades de trabajo, y como podemos interpretarlos de la forma correcta. Para así ponderarlos y conseguir una puntuación más cercana al rendimiento real de un desarrollador en un proyecto. 2.5.1 Pull Requests Esta unidad de trabajo se asocia a la persona que ha creado una petición de validación. Está caracterizado por: Título, descripción, commits, líneas añadidas, líneas modificadas, ficheros modificados y finalmente si se ha Marco Teórico 12 aprobado o no después de la revisión. Veamos qué impacto tienen estas variables en el valor final del pull request. 2.5.1.1 Título y descripción Un pull request sin título o descripción no debería ser considerado de manera positiva. El trabajo en equipo es fundamental en el desarrollo software y no explicar o documentar una tarea influye a la hora de revisar el código, ya que el desarrollador que lo hace debe averiguar por su cuenta en qué consisten los cambios que el autor del pull request ha realizado. En repositorios de código abierto un factor muy importante para que el pull request no se acepte y se fusione con la versión principal, es tener una descripción incompleta [11]. 2.5.1.2 Commits Un pull request, al ser una rama puede contener varios commits. Los commits tienen también un título, por lo que le pasa igual que al punto anterior. También, según un estudio, se puede vincular que los commits que no contienen un mensaje descriptivo están relacionados con fallos de compilación [12]. Además, se puede relacionar el tipo de tarea según el mensaje del commit, por palabras claves como error, añadir, arreglo, introducción… 2.5.1.3 Líneas y ficheros modificados Estos datos como hemos visto antes, por sí solos, no nos dicen nada. Un pull Request que modifique muchos archivos añada y borre muchas líneas de código puede repercutir de forma negativa en el tiempo que se tardará en revisarlo y con ello en terminar la tarea. En varios estudios [13] [14] sobre la experiencia de los desarrolladores de Mozilla [15] y Shopify Inc sobre cómo veían la revisión de código, se preguntó sobre cuales creían que eran los factores que más repercuten en el tiempo de revisión. Se obtuvo como factor más importante el volumen de líneas modificadas y en tercera posición en el ranking el número de ficheros modificados. Al mismo tiempo, también se les preguntó sobre qué elementos influyen en la calidad de las revisiones, determinando que el número de líneas era el segundo motivo que tenía un fuerte impacto en la calidad de éstas. Es decir, a mayor número de líneas que revisar, menor era la calidad de la revisión, y esto puede llevar a la introducción de fallos en el código. Esto también se pudo ver en otro caso de estudio en los equipos de desarrollo de Cisco [16] se encontró que a medida que aumentaban las líneas de código que revisar, menos comentarios recibían y menos fallos se localizaban. A partir de las 400 líneas de código la calidad de la revisión disminuye considerablemente. Por lo que podemos decir que los pull request que son más pequeños son revisados antes y con más atención que los que modifican muchas líneas y ficheros al mismo tiempo. 2.5.1.4 Si se ha fusionado o no el pull request con la rama principal Si se ha aceptado o rechazado el trabajo de un desarrollador es un parámetro a tener en cuenta a la hora de evaluar el conjunto de tareas que haya realizado. Figura 12: Factores que influyen en los tiempos de revisiones, en primer lugar, líneas modificadas. 13 13 2.5.2 Revisión La revisión de código en un equipo de desarrollo software es una buena práctica, hace que el equipo se comunique, se evitan fallos y se puede mejorar el código. Esta técnica está muy bien valorada entre los desarrolladores [16]. Es uno de los procesos imprescindibles en el modelo pull-based, y por eso es una de las unidades de trabajo que vamos a evaluar. Figura 13: Motivación de los desarrolladores para hacer revisiones. En los entornos de desarrollo actuales, la revisión de código debe ser un componente imprescindible en los procesos ya que nos aporta beneficios como: mejorar la calidad del código, mejorar la comunicación entre los desarrolladores, educar en buenas prácticas a los programadores que están empezando… Estos beneficios repercuten positivamente en un código más mantenible, tiempos más cortos de desarrollo y finalmente en ahorro de costes. Poder mostrar si este proceso se está llevando a cabo podría ayudar a conocer mejor cómo se está trabajando, de manera que podamos fomentar una cultura de revisión en nuestros equipos. GitHub nos da la opción de dejar un comentario en el pull request que estamos revisando. El número de comentarios debe ser considerado en las métricas, ya que demuestra la implicación de un desarrollador a la hora de revisar un pull request. Las revisiones se crean en la interfaz gráfica de GitHub y se pueden dejar también en líneas concretas del Pull Request. Marco Teórico 14 Figura 14: Interfaz gráfica de GitHub para dejar comentarios en líneas de código Como dato, puede marcarse como aprobado un Pull Request sin dejar ningún comentario, esto no debería ser considerado una buena revisión. 2.5.3 Revisiones con commit Se ha separado este tipo de revisión como unidad de trabajo ya que conlleva más implicación en la revisión que simplemente comentar en la interfaz de GitHub. Hemos visto en el proceso en detalle que el mismo revisor puede descargarse la rama en su repositorio local, hacer modificaciones y enviarlas al remoto, es decir, incluir commits propios en el Pull Request de otra persona como revisión. Si una persona ha revisado la tarea y ha añadido código, podemos decir que ha entendido la tarea, y que se ha implicado en añadir más valor o corregir algún fallo del autor de esta. Es por esto por lo que se separa como una revisión especial y la llamamos “Revisiones con commit”, porque es muy interesante conocer qué personas en el equipo son comprometidas en la revisión. En el commit podemos valorar algunos de los parámetros que ya vimos para pull requests, obtendremos su título, sus líneas y ficheros modificados. 15 15 3 ALGORITMO Y VISUALIZACIONES 3.1 Introducción En este capítulo detallaremos las visualizaciones y métricas que se han propuesto en los objetivos. Es necesario también explicar el algoritmo que considerará las métricas destacables del apartado anterior y así justificar el cálculo que muestra el impacto de los pull requests. 3.2 Visualización General La métrica principal que se obtiene del algoritmo explicado en este capítulo (OBJ-01) se muestra en una visualización (OBJ-02) que consiste en una sección por desarrollador que incluye círculos que representan las unidades de trabajo realizadas. Es una visualización que ayuda a ver en conjunto cuánto se ha hecho. Pero la cuestión está en representar también el impacto de cada una de esas unidades de trabajo. Para ello el tamaño del círculo representará el impacto. Este tamaño se calcula con un algoritmo que considera las métricas que seguían buenas prácticas explicadas en los apartados anteriores. Figura 15: Ejemplo de visualización principal En el ejemplo de la Figura 15 se muestra la evolución de un desarrollador en el que cada cuadrado representa un mes y los círculos unidades de trabajo. Se suele empezar con pocas contribuciones de poco valor y se acaba haciendo mucho más y de más valor. En este apartado definiremos qué tipo de elemento representará cada unidad de trabajo y cómo calcularemos el tamaño de la circunferencia. 3.2.1 Cálculo del impacto En primer lugar, vamos a estimar el tamaño del círculo que representará el impacto de un pull request. Queremos calcular una puntuación base a la que sumemos o restemos puntos considerando que contengan o no elementos de buenas prácticas vistos anteriormente. Algoritmo y visualizaciones 16 Partiremos de una ecuación general que desarrollaremos en este apartado: 𝑃𝑒𝑠𝑜𝑇𝑜𝑡𝑎𝑙=𝐹(𝑚𝑜𝑑𝑖𝑓𝑖𝑐𝑎𝑐𝑖𝑜𝑛𝑒𝑠,𝑎𝑟𝑐ℎ𝑖𝑣𝑜𝑠)+𝐺(𝑙í𝑛𝑒𝑎𝑠𝑎ñ𝑎𝑑𝑖𝑑𝑎𝑠,𝑙í𝑛𝑒𝑎𝑠𝑏𝑜𝑟𝑟𝑎𝑑𝑎𝑠)+𝑉 Cada una de estas variables significa: • Modificaciones: la diferencia entre líneas borradas y líneas añadidas. • Archivos: el número de ficheros modificados. • Líneas añadidas: Líneas nuevas insertadas en el código • Líneas borradas: líneas borradas en el código. La primera función estima una puntuación inicial en función de los archivos y la diferencia entre líneas borradas y modificadas. 𝐹(𝑚𝑜𝑑𝑖𝑓𝑖𝑐𝑎𝑐𝑖𝑜𝑛𝑒𝑠,𝑎𝑟𝑐ℎ𝑖𝑣𝑜𝑠)=𝑎𝑟𝑐ℎ𝑖𝑣𝑜𝑠∗𝑚𝑜𝑑𝑖𝑓𝑖𝑐𝑎𝑐𝑖𝑜𝑛𝑒𝑠 𝑎𝑟𝑐ℎ𝑖𝑣𝑜𝑠+1+𝑚𝑜𝑑𝑖𝑓𝑖𝑐𝑎𝑐𝑖𝑜𝑛𝑒𝑠 Las modificaciones pueden valer cero en el caso que tengamos exactamente las mismas líneas añadidas que borradas, por ejemplo, en el caso de que hagamos un buscar y reemplazar en muchos ficheros, el número de modificaciones es el mismo siempre. Nos quedan otros valores en la función del calculo que suman puntos. Con las métricas anteriores vemos que las líneas de código son un parámetro crítico ya que influye en la calidad y tiempo de revisión. Un número óptimo de líneas modificadas para tener una buena revisión estaría sobre las 250 líneas [16]. Este primer valor que obtendremos será a través de la variable modificaciones. Cuando este número es enorme, el algoritmo en esta función debe valorarlos de manera logarítmica, en vez de escalar. Las modificaciones automáticas como buscar y reemplazar no aportan mucho valor. Por ejemplo, cambiar 2000 líneas se establecería en aproximadamente 8 puntos. Para compararlo con este límite, 250, se lleva a cabo una simple comparación: si el número de modificaciones es mayor de 250 o el número de ficheros mayor de 100 se considera que debemos calcular el impacto de manera logarítmica. Lo que nos ayudaría a estabilizar la puntuación que se obtiene en esta función. 𝐹(𝑚𝑜𝑑𝑖𝑓𝑖𝑐𝑎𝑐𝑖𝑜𝑛𝑒𝑠,𝑎𝑟𝑐ℎ𝑖𝑣𝑜𝑠)=log⁡( 𝑎𝑟𝑐ℎ𝑖𝑣𝑜𝑠∗𝑚𝑜𝑑𝑖𝑓𝑖𝑐𝑎𝑐𝑖𝑜𝑛𝑒𝑠 𝑎𝑟𝑐ℎ𝑖𝑣𝑜𝑠+1+𝑚𝑜𝑑𝑖𝑓𝑖𝑐𝑎𝑐𝑖𝑜𝑛𝑒𝑠) Figura 16: Densidad de fallos encontrados conforme aumenta LoC(Líneas de código) 17 17 La segunda función es una puntuación del número de líneas diferentes que hay una vez el pull request es fusionado. Es decir, el valor absoluto de la diferencia entre las líneas añadidas y las líneas borradas. Esta puntuación será muy fácil de calcular y también se estimará con un logaritmo ya que es necesario que a mayor número de líneas modificadas crezca el impacto total de la tarea, pero sea continua en valores altos. 𝐺(𝑙í𝑛𝑒𝑎𝑠𝑎ñ𝑎𝑑𝑖𝑑𝑎𝑠,𝑙í𝑛𝑒𝑎𝑠𝑏𝑜𝑟𝑟𝑎𝑑𝑎𝑠)=log⁡(|𝑙í𝑛𝑒𝑎𝑠𝑎ñ𝑎𝑑𝑖𝑑𝑎𝑠 −𝑙í𝑛𝑒𝑎𝑠𝑏𝑜𝑟𝑟𝑎𝑑𝑎𝑠|+1) La última variable de la ecuación será una serie de puntos adicionales que se sumarán o restarán en función de que el pull request cumpla una serie de requisitos que hemos visto en el apartado Métricas destacables. Tabla 6: Valores posibles de la variable V y su descripción Variable Descripción No contiene Contiene Título Tarea Si el pull request carece de título, o sólo tiene una palabra se considera una mala práctica. -1 1 Descripción Es una buena práctica que este campo no esté vacío. Ayuda al revisor a entender la tarea lo que conlleva que se agilice el proceso de revisión. -1 1 Commit ‘Merge Master’ Si en el commit se incluye ‘merge master’ simplemente significa que hemos añadido los cambios de la rama principal a la nuestra por lo que este commit no debería influir en el algoritmo -1 0 Commits vacíos Si contiene commits vacíos sin descripción 1 -1 Algoritmo y visualizaciones 18 El cálculo del impacto se ha descrito hasta aquí, ahora pasaremos a describir cómo se distinguen los tipos de trabajos en la visualización: Tabla 7: Colores y significado de la visualización principal Elemento Descripción Tipo Círculo amarillo Este elemento representa las revisiones del tipo comentario Revisión Círculo vacío Este elemento significa que la persona en cuestión ha añadido un commit a una rama que no era suya Revisión Círculo Rosa Este elemento representa una tarea que todavía no se ha cerrado o fusionado con la rama principal. Pull Request Círculo celeste Este elemento representa una tarea que se ha finalizado. Pull Request Un ejemplo, de la visualización principal podría ser este: Figura 17: visualización de ejemplo de un desarrollador. Esta visualización ayuda a un gestor a determinar varios elementos que pueden ayudarle: • Detectar las contribuciones de una persona de manera general de un simple vistazo • Detectar qué perfiles son más proactivos en la revisión o qué perfiles menos. • Determinar la magnitud de las contribuciones que se están realizando, gracias a los tamaños de los círculos. • Motivar a los desarrolladores con las visualizaciones ya que se han pensado con este aspecto para que sean más amigables. • Determinar si se están haciendo contribuciones muy grandes y se deben hacer tareas más pequeñas. 19 19 • Determinar la evolución de un desarrollador. Ejemplo: Una representación de un proyecto real de código abierto: Stripe, una famosa plataforma de pago online. La visualización nos muestra un cuadrado por desarrollador, de un solo vistazo podemos conocer la dinámica de este equipo: vemos que solo el usuario remi-stripe es el que hace pull requests los otros dos desarrolladores se enfocan más en revisar las tareas. Probablemente, el usuario con un solo pull request habrá hecho una contribución puntual, esto es debido a que el proyecto que estamos analizando es de código abierto y cualquier persona podría crear un pull request. Figura 18: Varios desarrolladores y sus contribuciones a un proyecto de código abierto 3.3 Visualización: tiempo en terminar las tareas Esta visualización tendrá dos modos: • Tiempo desde que se abre un pull request hasta que se cierra o se fusiona con la rama principal. • Tiempo desde que se empieza una revisión hasta que se fusiona o cierra el pull request. Los objetivos de esta visualización son los siguientes: • Mostrar qué tareas están tardando más tiempo en ser finalizadas. De forma que podamos detectar si tal vez es necesario separar tareas muy grandes en tareas pequeñas, o si un desarrollador está desmotivado o tiene problemas con esa tarea en concreto. • Mostrar qué revisiones tardan más en concluirse, esto puede significar que no se aplican los cambios que se piden en la revisión o que se están haciendo Pull Requests demasiado grandes y tediosos de revisar. • Enseñar qué tareas pueden estar siendo conflictivas en los ciclos de revisión. Figura 19: Visualización que muestra cuanto se ha tardado en realizar tres pull requests Las visualizaciones son dinámicas por lo que en el ejemplo de la Figura 19 veremos los títulos de las tareas al Tecnologías usadas 26 Código 3: Ejemplo de función reductor que recibe como parámetros el estado y la acción lanzada. En nuestra aplicación Redux nos ayudará a compartir entre componente los datos que usaremos para llevar a cabo los cálculos del algoritmo además de propiedades que les pasaremos a los componentes. De forma que el esquema resumen del funcionamiento de React + Redux es el siguiente: Figura 23: React + Redux esquema resumen 4.1.4 D3.js Para crear las visualizaciones hemos usado D3 [20]: también es una librería de JavaScript que permite crear gráficos basándose en HTML, CSS y SVG. Es una de las más populares actualmente. También es de código libre, haciendo así mucho más fácil encontrar respaldo de una comunidad que mantiene esta librería. 4.1.5 API GitHub + GraphQL API, Application Program Interface o interfaz de programación de aplicaciones en español, define el conjunto de funciones, conectores y reglas que un desarrollador debe seguir si quiere interactuar con los datos y funciones de otro software. Se ofrece como una capa de abstracción, de forma que simplifica el trabajo de programación. La API de Github [22] permite obtener la información sobre el trabajo de los desarrolladores en un repositorio. GitHub ofrecía un modelo API REST y migró al modelo GraphQL en 2016 [24] GraphQL es una nueva forma de ver las API, para explicarlo podemos separarlo en dos secciones: - El lado cliente: usa un lenguaje de consultas [23], con el que podemos hacer consultas específicas de datos. Esto comúnmente se ha visto en consultas SQL, pero siempre en el lado del servidor, con esta tecnología podemos hacer consultas desde el lado del cliente. - El lado servidor: entorno de ejecución que procesará esas consultas y devolverá los datos, en nuestro caso es GitHub. GraphQL es completamente independiente del lenguaje y base de datos usados, puede ser una base de datos SQL, o no relacional, es más podría ser una API REST. Con respecto a los lenguajes, es compatible con JavaScript, PHP… Store Almacena los datos y provee la fución dispatch Interfaz Se actualiza en función de los datos de la Store. Reducers Interpretan las acciones lanzadas por la Interfaz. Devuelve los nuevos valores de Store. store.dispatch(acción) 27 27 La principal diferencia que tenemos con un API REST es que el cliente tiene muy limitados los parámetros de la petición, las opciones son muy rígidas, mientras que en GraphQL, al tener un lenguaje de consultas en el cliente, se puede pedir más información con mucha más flexibilidad. En GraphQL podríamos hacer Joins de la información en una sola petición, mientras que en REST tendríamos que unir nosotros la información de varias peticiones. Por ejemplo, en la Figura 24 si quisiéramos obtener la información de un autor y la información sobre el equipo al que está asociado, el cliente necesita hacer dos peticiones: primero pediríamos el autor y con el identificador del equipo al que pertenece, después pediríamos la información del equipo. GraphQL usa un sistema de tipos para poder construir la estructura de la información que ofrece el servidor. • Queries: Representan la petición de datos. • Mutations: permiten modificar, borrar o actualizar datos de la base de datos del servidor. • Subscriptions: Son eventos en tiempo. En este proyecto solo utilizaremos Queries ya que lo que queremos es consultar la información, es decir hacer peticiones de datos. A este conjunto, queries, mutations y subscriptions, se le llaman Schema, define al cliente cómo debe consultar la información. Cuando un cliente se quiere conectar a ese Schema para hacer peticiones estará definido cómo debe hacerlo en la documentación de la API. Se puede decir que el Schema es el modelo de datos usado en la interfaz cliente servidor. Los tipos, definidos anteriormente, se conectan con los Resolvers que se conectan con el gestor de datos que se tenga, que puede ser SQL, no relaciona, GraphQL, api rest… estos Resolvers son funciones que usan el Schema y que devuelven al cliente los datos pedidos en la consulta. Figura 25: Funcionamiento del servidor que ofrece una API GraphQL Figura 24: Modelo API REST de GitHub Tecnologías usadas 28 Por lo tanto, al poder el cliente construir la petición con todos los datos necesarios se ahorra conexiones con el servidor. En el ejemplo anterior, si usamos GraphQL, el cliente sólo tiene que hacer una petición. Sólo tiene que construir la consulta en función del Schema que se le ofrece y el servidor le contestará con toda la información que ha pedido. En nuestro caso es una gran ventaja ya que necesitamos consultar muchos datos. GraphQL nos ayuda a simplificar las peticiones y poder recibir la información agregada. Figura 27: Ejemplo de consulta y resultado en GraphQL a la API de GitHub En la Figura 27 podemos ver una petición donde según el autor pedimos su id, su avatar y su nombre de usuario además pedimos sus diez últimos repositorios y un dato de estos repositorios. Como resultado nos da en formato JSON toda esta información unificada. 4.1.6 Jest Jest [19] es una librería más de JavaScript que nos permite crear tests con los que verificar que el código se ejecuta de manera correcta. Su sintaxis es sencilla y se implementa a través de scripts en JavaScript. Se puede testear tanto la interfaz como las funciones. Se ejecuta a través de la terminal de comandos. Con esta herramienta se pueden crear test unitarios que comprueben los árboles de React y verificar si el estado de la aplicación es el esperado. Jest provee al usuario una sintaxis muy intuitiva, con la que construir los tests. En su documentación encontramos todos los métodos que podemos usar para codificar el test. Los nombres de las funciones que comprueban son muy intuitivos. Para entender mejor el funcionamiento de esta tecnología y cómo la vamos a usar en este proyecto vamos a explicarlo con un ejemplo[19] : Suponemos que nuestra aplicación tiene una función llamada suma que acepta dos parámetros, los sumandos, y devuelve el resultado de la suma. Tal como vemos en el siguiente código: function suma(a, b) { return a + b; } module.exports = suma; Figura 26: Modelo GraphQL de GitHub 29 29 Código 4: Definición de una función suma simple. Si queremos crear una prueba unitaria para comprobar que la función suma se comporta de manera adecuada, crearemos un test en el archivo suma.test.js que verificará esta función con Jest. Para ello seguiremos los siguientes pasos: 1) Para poder usar la función suma importamos el archivo donde se encuentra la declaración de la función a probar. const suma = require('./suma'); Código 5: Importación del archivo donde se encuentra la función suma 2) Usamos las funciones test, expect y toBe proporcionadas por Jest. En primer lugar, test acepta dos parámetros: una cadena de caracteres para describir el test y una función, que, en nuestro ejemplo, llamará a la función suma y comprobará el resultado. test('Sumar 1 + 2 debe ser igual a 3', () => { expect(suma(1, 2)).toBe(3); }); Código 6: Declaración del test para la función suma Para declarar la función que comprobará suma usamos la función expect que acepta un parámetro, la función que debe ejecutar el test. Después usamos toBe, esta función recibe el resultado de suma y compara con el resultado que hemos declarado, en este caso 3. 3) Al ejecutar el script suma.test.js con Jest el resultado que debe darnos como salida en la terminal de comandos debe ser el siguiente PASS ./suma.test.js ✓ sumar 1 + 2 debe ser igual a 3 (5ms) Código 7: Resultado correcto de la ejecución del test para la función suma. Si el test no hubiera funcionado nos saldría error en rojo. En este trabajo esta tecnología nos será de gran ayuda en la etapa de pruebas, para ejecutar tests unitarios de manera automática y probar que las funciones que hemos programado funcionan de manera adecuada, además también nos servirá para comprobar que Redux contiene los valores correctos después de lanzar una determinada función. 4.1.7 SDK de Autentificación y Autorización de Firebase Es un servicio gratuito de Google [25] que nos sirve para llevar a cabo la integración de la autentificación y autorización de GitHub. La autentificación identifica al usuario y la autorización determina qué puede hacer. Necesitamos identificar al usuario de GitHub y determinar los permisos que tiene sobre la api. Esto permitirá a los usuarios acceder a la aplicación web mediante su cuenta de GitHub. Google proporciona una serie de funciones que tienen ventajas para el desarrollo de la aplicación y para el usuario final: • Es más seguro usar un servicio de autentificación externo, ya que este servicio brinda de manera sencilla y segura los métodos necesarios para gestionar usuarios. Evitando que tengan que desarrollarse métodos propios de seguridad desde cero. • Para el usuario final la experiencia de usuario es mucho más rápida y sencilla, no necesita crear una cuenta con una nueva contraseña ni rellenar formularios de registro. Solo tiene que dar acceso a su cuenta y aceptar los permisos que se le piden. Tecnologías usadas 30 Figura 28: Ventana emergente de autorización para Github También este servicio nos crea una base de datos con los emails que han iniciado sesión en la aplicación. Esta se crea de forma automática pero no la utilizaremos en este proyecto. 4.1.8 Visual Studio Code Como entorno de desarrollo hemos usado Visual Studio Code de Microsoft [26]. Ofrece complementos que personalizan sus funcionalidades como la detección de errores de programación, formateo rápido, autocompletado inteligente, integración con Git… Es gratuito y de código abierto. Por supuesto es compatible con el lenguaje que hemos usado para el desarrollo de la aplicación web. 4.1.9 Herramientas para depurar el código Para poder depurar el código y resolver problemas, hemos utilizado un plugin para el navegador Google Chrome llamado “React Developers Tool”. Esta herramienta nos permite: • Comprobar las propiedades de los componentes de nuestro código • Comprobar el estado de Redux • Lanzar acciones desde su interfaz y comprobar cuáles se han lanzado. 31 31 Figura 29: Interfaz de React Developers Tool en funcionamiento Como complemento para poder parar la ejecución del código y trazar las variables hemos usado la consola que viene por defecto con el navegador Google Chrome. Se puede acceder en todos los navegadores Google Chrome pulsando F12. Al tener la aplicación corriendo en localhost, accedemos con el navegador a la url localhost:3000 y con esta herramienta podemos ver todo el código de nuestra aplicación en tiempo real. Además, permite trazar el código añadiendo puntos de parada, y ejecutar comandos en la consola que aparece en la parte inferior. Esto es muy cómodo a la hora de conocer qué valores tienen las variables en cierto punto de la ejecución del código. Figura 30: DevTools de Google Chrome. Ejemplo de un punto de parada en el código. 4.2 Herramientas de organización y control de versiones En este apartado vamos a ver herramientas de organización de tareas y control de versiones que se han usado para el desarrollo del proyecto. Tecnologías usadas 32 4.2.1 Git & GitHub A la hora de organizar y tener almacenado el código de la aplicación se ha usado GitHub. Y como método de control de versiones Git. 4.3 Herramientas de integración continua y despliegue 4.3.1 CircleCI Esta herramienta se integra con GitHub y nos permite automatizar la ejecución de las pruebas que hemos programado con Jest. La hemos integrado con el repositorio remoto y como consecuencia cada vez que hay un nuevo commit o una nueva rama los tests se vuelven a ejecutar y nos alerta a través de la interfaz gráfica si los últimos cambios han pasado los test o no. Figura 31: Mensaje que aparece en la interfaz gráfica de GitHub integrado con CircleCI cuando los tests no son correctos. Para integrar esta herramienta con GitHub hemos tenido que añadir al sistema de ficheros de nuestro proyecto un fichero de configuración con el nombre circle.ci, que explicamos en más detalle a continuación. (Se resaltan los comentarios en gris para explicar código concreto) # Javascript Node CircleCI 2.0 configuration file # # Check https://circleci.com/docs/2.0/languagejavascript/ for more details # version: 2 jobs: build: docker: #Versión de Node que queremos usar - image: circleci/node:10.9.0 #directorio donde debe situarse CircleCI working_directory: ~/repo steps: - checkout # Comprueba si han cambiado el package.json para instalar nuevas dependencias o no keys: - v1-dependencies-{{ checksum "package.json" }} - v1-dependencies- 33 33 # Comando para instalar las dependencias - run: yarn install - save_cache: paths: - node_modules key: v1-dependencies-{{ checksum "package.json" }} # Comando para ejecutar los tests - run: npm run test Código 8: Fichero de configuración de CircleCI 4.3.2 Vercel Esta herramienta es un servicio que también se integra con GitHub y nos permite compilar y desplegar, en los servidores de Vercel, la aplicación para poder testear de forma manual la misma. Si hay algún fallo en la compilación también manda alertas tanto por email como por la interfaz de GitHub. Por cada rama nueva se hace un despliegue, es decir en cada Pull Request tendremos la posibilidad de testear manualmente gracias al este servicio Figura 32: Mensaje integrado en un Pull Request de la integración de Vercel con GitHub En la Figura 32 al pulsar en View deployment nos llevará a una url temporal donde estará desplegada esa rama del repositorio, es decir una versión de la aplicación, en este caso por ejemplo sería una url de este formato https://scope-aatz3gjfpt.now.sh/ Es una herramienta muy útil a la hora de revisar nuevas funcionalidades ya que no se necesita descargar al repositorio local los cambios, compilar y desplegar en localhost para comprobar si una rama está funcionando o no. Además, evita tener servidores de pruebas, porque ellos se ocupan del despliegue automático después de configurarlo una primera vez. Para llevar a cabo la integración con GitHub es necesario añadir un archivo de configuración en el directorio raíz de nuestra aplicación, el archivo es el siguiente: - Now.json: este fichero define los parámetros de configuración del servicio. Vercel usará estas variables para compilar nuestra aplicación Le pasamos en el campo builds los parámetros necesarios: src es el nombre del archivo del que instalará las dependencias, use es la librería que usará Vercel para compilar la aplicación. Y por último hay que especificar en el parámetro config/distDir donde estará la carpeta que queremos desplegar, en nuestro caso esa carpeta se llama build. { Tecnologías usadas 34 "version": 2, # Nombre del despliegue "name": "Scope Testing", "builds": [ { "src": "package.json", # Módulo del servicio Vercel que compilará la aplicación "use": "@now/static-build", # Directorio donde se crea la aplicación que se desplegará "config": { "distDir": "build"} } ] } Código 9: Fichero de configuración del servicio Vercel. De este modo Vercel conoce los parámetros necesarios para desplegar la aplicación y podremos administrar este servicio desde su interfaz web. Por ejemplo, al crear un pull request aparecerán este mensaje que nos facilita dos enlaces: Figure 33: Interfaz gráfica de Github integrado con Vercel. 35 35 - Preview: te lleva al despliegue en sus servidores de la aplicación para poder testearla manualmente. - Inspect: te lleva a su herramienta para poder ver los logs de esa compilación y despliegue en detalle. Figure 34: Herramienta Vercel Logs del despliegue de una rama en detalle. En otro menú de la herramienta Vercel podemos acceder al despliegue que se ha realizado por cada rama y nos da su estado y el enlace para poder probar la aplicación. Figura 35: Interfaz gráfica del servicio Vercel, lista de despliegues. Este servicio es gratuito para proyectos pequeños, si se quiere personalizar el dominio donde se despliega ya entra en un modelo del servicio de pago. Especificación de Requisitos 42 Tabla 21: Requisitos de conducta: RFC_003 - Gráfico de ficheros más modificados RFC_003 Visualización de ficheros más modificados Versión Versión 1.0 Dependencias • OBJ_05: Visualización ficheros más modificados. Requisitos hijos • RFC_006: Leyendas. Descripción Esta visualización debe mostrar los ficheros que más se han modificado del proyecto. A más modificaciones más intensidad de color debe tener la ruta del archivo. Prioridad Alta Comentario Tabla 22: Requisitos de conducta: RFC_004 - Gráfico estimación duración de las tareas RFC_004 Visualización estimación duración de las tareas Versión Versión 1.0 Dependencias • OBJ_04: Visualización del tiempo en terminar tareas. Requisitos hijos • RFC_006: Leyendas. Descripción Esta visualización debe mostrar, en horas, la duración media desde que un pull request se abre hasta que se fusiona con la rama principal. Prioridad Alta Comentario 43 43 Tabla 23: Requisitos de conducta: RFC_005 - Pantalla de carga RFC_005 Pantalla de carga Versión Versión 1.0 Dependencias • RFC_003: Gráfico de ficheros más modificados • RFC_002: Gráfico de red de nodos simple • RFC_004: Gráfico estimación duración de las tareas Descripción Es necesario mostrar que se están cargando los datos al usuario. Esto se consigue mediante la interfaz, en el caso de que estemos pidiendo datos a la API de GitHub debe mostrarse un elemento dinámico que de a entender que el sistema está cargando. Prioridad Media Comentario Tabla 24: Requisitos de conducta: RFC_006 - Leyendas RFC_006 Leyendas Versión Versión 1.0 Dependencias - Descripción Es de vital importancia hacer la aplicación al usuario fácil de entender, por cada una de las visualizaciones se mostrará qué representan los tamaños, las formas, los colores, etc. es decir, mostrar una leyenda. Prioridad Media Comentario Tabla 25: Requisitos de conducta: RFC_007 - Desplegar información del gráfico de nodos. RFC_007 Desplegar información del gráfico de nodos Versión Versión 1.0 Dependencias - Descripción El usuario debe poder saber qué tareas ha hecho cada desarrollador, a partir del gráfico de nodos simple. Para ello la vista cambiará a una lista simple con el título de la tarea y la puntuación que se le ha estimado. Esta lista debe contener enlaces a los pull requests en GitHub, es decir que al pulsar en alguno de ellos te lleve a ese pull request en la web de GitHub. Prioridad Media Especificación de Requisitos 44 Comentario 5.5 Requisitos no Funcionales Los requisitos no funcionales no hacen referencia directa a las funciones específicas del Sistema a nivel usuario. Estos requisitos se refieren a las características del sistema como rendimiento, seguridad, disponibilidad. 5.5.1 Requisitos de Fiabilidad Tabla 26: Requisitos de Fiabilidad: RNF_001 - Disponibilidad de los datos RNF_001 Disponibilidad de los datos Versión Versión 1.0 Dependencias • OBJ_02: Visualización general. • OBJ_03: Visualización Individual. • OBJ_04: Visualización del tiempo en terminar tareas. • OBJ_05: Visualización ficheros más modificados. Descripción La API de GitHub garantiza una disponibilidad del 99%. Nuestro sistema guarda los datos en la caché del navegador del cliente. Por lo que, si en siguientes peticiones GitHub fallara, tendremos una copia en caché de los datos que se puede mostrar. Aunque estos datos seguirán siendo los de la última consulta realizada con éxito. Prioridad Alta Comentario Todas las visualizaciones dependen de este requisito ya que sin datos no podemos mostrar nada. 5.5.2 Requisitos de Seguridad Tabla 27: Requisitos de seguridad: RNFS_001 - Autentificación y Autorización RNFS_001 Autentificación y Autorización Versión Versión 1.0 Dependencias • RG_001: Autentificación/Inicio de Sesión. Descripción Se debe garantizar que la autorización y autentificación sea fiable. Se utilizará Google como proveedor de servicio para llevar a cabo esta funcionalidad. Se deberá avisar al usuario, en todo momento, que sus datos son totalmente privados y se quedan en su navegador. Y que en ningún momento nuestro sistema guarda datos del proyecto analizado. 45 45 Prioridad Alta Comentario - Tabla 28: Requisitos de seguridad: RNFS_002 - Borrado de datos al cerrar sesión RNFS_002 Borrado de datos al cerrar sesión Versión Versión 1.0 Dependencias • RG_001: Autentificación/Inicio de Sesión. Descripción Es importante que después de cerrar sesión se eliminen los datos del usuario que están en su navegador, así como el estado de Redux. En el estado de Redux se encuentra información sensible como es el token de autorización y no es conveniente que se quede guardado si el usuario ya no está dentro de la aplicación. Prioridad Alta Comentario - 5.5.3 Requisitos de Eficiencia Tabla 29: Requisitos de eficiencia: RNFE_001 - Tiempo de respuesta menor en posteriores peticiones RNFE_001 Tiempo de respuesta menor en posteriores peticiones Versión Versión 1.0 Dependencias • RNFF_001: Disponibilidad de los datos. Descripción El tiempo de respuesta debe ser menor de 2 segundos si los datos ya se han consultado con anterioridad. Ya que debe usar parte de los datos en caché. Prioridad Media Comentario Diseño del sistema 46 6 DISEÑO DEL SISTEMA Este apartado tiene como objetivo describir cómo está diseñado el sistema. Además de definir cómo se comporta el mismo con los sistemas externos, proveedores de servicios y la API. 6.1 Diagramas de paquetes Este diagrama nos muestra los paquetes que componen el sistema y su relación con los servicios externos, de forma que podemos ver cómo se compone el sistema en su totalidad de una forma simplificada. Diagrama 1: Diagrama de paquetes del sistema y relación con servicios externos. 47 47 Aquí se puede ver que en la aplicación se requiere un cliente Firebase que usará los servicios proporcionados por Google Firebase. Este es el encargado manejar la autorización y autentificación y devolver los datos necesarios a la aplicación web. 6.2 Diagrama de componentes Como bien definimos en las tecnologías usadas, React se organiza en componentes de forma jerárquica, este diagrama describe qué componentes conforman la aplicación. En este diagrama podemos distinguir las propiedades (props) que son las variables del componente, éstas se pueden pasar de un componente a otro, cómo es el caso de la relación entre el componente Layout y Footer. En el caso de los demás componentes estos se conectan con Redux y las propiedades son las que se pasan a través de la Store. Es mejor conectar el Store de esta forma ya que si usamos mucho que las propiedades se pasen de unos componentes a otros puede causar fallos de rendimiento y actualizaciones de componentes innecesarias. En el caso de que el componente use un estado propio (además del store de Redux) se detalla en localState. Diagrama 2: Diagrama general de componentes Diseño del sistema 48 6.3 Modelo del estado local La información que muestra la aplicación se encuentra en Redux. Recordamos que este estado contendrá dos objetos principales: user y repos, como ya se especificó en los requisitos de información. En el siguiente diagrama se define cómo será el estado inicial que tendrá la aplicación, las variables que guardará en Redux y los tipos de las mismas: Diagrama 3: Diagrama de objetos del estado de Redux. En el siguiente diagrama podemos ver un ejemplo de los valores por defecto que tendrá el estado de Redux si no se ha iniciado sesión. 49 49 Diagrama 4: Diagrama de objeto del estado de la aplicación antes de iniciar sesión. 6.4 Diseño por casos de uso 6.4.1 CU.001: Inicio de sesión En este caso vamos a describir este proceso con un diagrama de secuencia ya que muestra de manera clara como es la interacción del usuario con el servidor de autorización (Google) y de este con el proveedor de identidad (GitHub). Para la aplicación el proceso de comunicación entre Google y GitHub es invisible. Si el usuario intenta iniciar sesión le aparecerá una ventana emergente que pedirá que inicie sesión con su cuenta de GitHub y acepte los permisos que necesita la aplicación para acceder a sus datos. Cuando acepta es Google el encargado de manejar este proceso con GitHub y devolvería a la aplicación el token. Después de obtener el token, es decir que el inicio de sesión se ha completado se cambiaría la interfaz al panel principal. Diagrama 5: Diagrama de Secuencia: Inicio de Sesión Diseño del sistema 50 6.4.2 CU.002: Cierre de sesión El siguiente diagrama de secuencia describe los mensajes que la aplicación intercambia con los servicios externos para cerrar sesión. El usuario cerrará sesión y la aplicación usará la función signOut que provee el SDK de Google para cerrar la sesión y desconectar al usuario. Como vemos en el diagrama es necesario borrar el estado local cuando se cierra sesión cumpliendo así los requisitos de seguridad detallados en RNFS_002. Diagrama 6: Diagrama de secuencia Cierre de Sesión 6.4.3 CU.003: Analizar un proyecto. Los diagramas de actividad sirven para representar la lógica de un algoritmo, describir los pasos de un caso de uso, simplificar y clarificar los procesos, además de poder modelar elementos de la arquitectura del software como funciones y operaciones. Nosotros lo vamos a usar para detallar qué lógica se da en la aplicación desde que se ha iniciado sesión hasta que se analiza un proyecto. Describiendo qué flujo se seguirá si hay fallos y si no los hay. Para que con este diagrama queden mucho más claros los pasos que se llevan a cabo para cubrir el requisito funcional detallado en RFC_001: Mostrar y gestionar errores. 51 51 Diagrama 7: Diagrama de actividad de la aplicación desde el inicio de sesión al análisis un proyecto. Implementación 58 Captura 9: Interfaz de la visualización de ficheros más modificados Además, al poner el ratón sobre los extremos de la visualización debe verse el nombre del fichero. 7.2.5 Alertas de errores La gestión de errores está definida en el requisito general de conducta RFC_001: Mostrar y gestionar errores,0donde están definidos los diferentes casos de error que se pueden dar en la aplicación. Es necesario que el usuario conozca qué tipo de error se ha producido, y la forma será la siguiente: Captura 10: Mensaje de error cuando no existe el proyecto que se ha buscado En esta captura se ha intentado analizar un proyecto que no existe en GitHub y el usuario lo sabrá por un mensaje en color llamativo en el centro de la pantalla. En la barra inferior aparecerá el título del proyecto que no se ha podido analizar correctamente también en color rosa. Hasta que no se deselecciona el proyecto, o se analice correctamente no se quitará el error de la pantalla. Los demás errores se mostrarán de la misma forma con el texto descriptivo. Captura 11: Barra de proyectos cuando uno no se ha podido cargar 59 59 8 PLAN DE PRUEBAS a fase de pruebas en el proceso software es muy importante ya que comprueba que se cumplen los requisitos tanto funcionales como los no funcionales y se verifica la calidad. En este documento se detallarán las pruebas se van a realizar. Se han realizado pruebas unitarias que comprueban que las funciones básicas del sistema se comportan correctamente. Las pruebas realizadas se han programado con Jest, descrita en Tecnologías usadas. Se han automatizado con la herramienta, también descrita, CircleCI. También se llevarán a cabo una serie de tests manuales para comprobar que la interfaz muestra correctamente los datos. Para ello nos hemos ayudado de los despliegues de Vercel, agilizando el proceso de comprobar funcionalidades nuevas sin tener que desplegar manualmente el código. 8.1 Configuración de los tests y ejecución Para el uso de tests en este proyecto hemos declarado dos ficheros scripts que contendrán los tests unitarios programados en Jest. • Layout.test.js: contendrá los tests para pruebas de interfaz • tests.js: contendrán los test que prueban las funcionalidades de la aplicación. Para ejecutarlos de manera automática con un solo comando, se ha añadido la opción script en archivo package.json, este se encuentra en la carpeta raíz del proyecto, contiene datos de configuración y las dependencias de la aplicación que se instalan al ejecutar npm install. Con esta configuración Jest sabrá que archivo es el que se debe ejecutar cuando queremos lanzar los tests: { "scripts": { "test": "node scripts/test.js --env=jsdom --forceExit" } } El resultado es que con solo llamar al siguiente comando tenemos Jest lanzado en el terminal. npm run test Cuando se ejecuta este comando aparecerá en la consola una serie de funciones a escoger, si queremos ejecutar todos los test a la vez nos interesará la opción press a, pero podemos escoger entre todas esas opciones dependiendo de las necesidades que tengamos en el momento. L Plan de pruebas 60 Figura 37: Salida del comando npm run tests 8.2 Pruebas unitarias 8.2.1 Introducción y descripción de los tests Hemos creado tests que verifiquen que el programa actúa correctamente al pedir y recibir los datos. De forma que podamos probar las casuísticas del programa vistas en los casos de usos. Hasta ahora hemos explicado cómo realizar la configuración e instalación de la herramienta Jest para integrar los test con nuestra aplicación y GitHub. Para un mejor entendimiento de estas pruebas unitarias vamos a describir cómo hemos llevado a cabo la codificación de los test. Vamos a detallar a nivel de código nuestras pruebas para comprender mejor esta sección. El fichero test.js contiene los test que describiremos en la siguiente sección, estas pruebas se enfocarán en probar las funciones del programa que piden y procesan los datos. En el apartado 4. Tecnologías Usadas: Jest probábamos la función suma() aquí probaremos las funciones de la aplicación. En primer lugar, vamos a ver cómo se configuran las variables que necesitaran los tests. // Necesitamos importar las funciones que vamos usar de los ficheros thunks y actions y la función //configureStore() // En thunks se encuentran las funciones que usa el programa import {configureTestStore} from '../configureStore' import * as thunks from '../thunks' import * as actions from '../actions' // Declaramos una variable global que guarde un token para hacer pruebas const REFRESH_TOKEN = “AE0u-NeO3u_IVtOuT1hnkcD88MC5J1ud-“ // Declaramos una variable que tenga datos de verdad de un usuario para hacer pruebas // No hay que modificarlo en cada sesión, para este test hemos usado nuestro usuario const testUser = { token: '3f6da023f139cb9e5df0fccd448ad6ad1e8958', photoURL: 'https://avatars3.githubusercontent.com/u/43379210?v=4', email: '[email protected]', username: 'maluzzz', refreshToken: REFRESH_TOKEN, } 61 61 Código 10: Fichero script para los tests parte I El token para los test lo hemos generado en a través de la interfaz de GitHub, de esta forma el token no caduca. Figure 38: Interfaz de GitHub para genera un token En estas pruebas Jest simula la ejecución del programa, por lo que necesitamos darle las variables necesarias, en este caso testUser y un token. Además, el programa centra todos sus movimientos en el Store, por lo que también es necesario usar la función configureStore(), esta función nos la provee el fichero configureStore.js, en este fichero hacemos uso de Redux para crear un Store inicial que contendrá todo lo necesario para que Jest pueda ejecutar la aplicación. Al usar esta función obtenemos el objeto Store que contiene los métodos necesarios como dispatch o getState, imprescindibles para la ejecución de estos tests A continuación, vamos a describir cómo funciona paso a paso uno de los tests 1) Usamos la función describe() que nos provee Jest, esta nos permite pasarle un título y un bloque de varios tests // Esta función nos permite ejecutar un bloque entero de tests describe('redux/repos/tests', () => { /*Bloque de tests*/ }) Código 11: Fichero script para los tests parte II 2) En el interior de esta función usaremos it(name, fn) que nos permite ejecutar un test, le pasamos como parámetros un título y fn hace referencia a la función que debe ejecutar Jest, si esa función contiene algún error fallará el test y nos lo dirá por pantalla. Esta función debe simular paso a paso las funciones que se ejecutan cuando un usuario usa la aplicación. De esta forma se ponen a prueba las funciones. Cuando las funciones se han ejecutado, ya por último, debemos comprobar que el resultado de la ejecución es el correcto y no otro. // PRIMER TEST: dentro de describe() empezaremos a llamar a las funciones que deben ejecutarse para // probar una funcionalidad concreta. it( // Descripción del test como primer parámetro 'Debe obtener los datos del proyecto público facebook/react', Plan de pruebas 62 async () => { // Obtenemos un store inicial const store = configureTestStore() // Lanzamos la acción de iniciar sesión de nuestro testUser store.dispatch(actions.userSignedIn(testUser)) // Lanzamos la acción de pedir los datos en este caso es // fetchSinbleRepoDataByLogin(nombreRepositorio) await store.dispatch(thunks.fetchSingleRepoDataByLogin('facebook/reac t')) // Llamamos al estado de la aplicación const state = store.getState() // Si ha ido bien la petición en el estado de la aplicación la variable erroredRepos debe estar vacía. expect(state.repos.erroredRepos).toEqual([]) }, 10000 // Temporizador, Si el tiempo de ejecución es mayor de 10 segundos se acaba el test y da // error ) }) Código 12: Fichero script para los tests parte III Como vemos en el código hemos definido paso a paso qué funciones se ejecutarán hasta llegar a obtener los datos de un proyecto de GitHub. En este test hemos llamado auserSignedIn(user) y fetchSingleRepoDataByLogin(nombreRepositorio) . De manera parecida a la que se muestra en Código 12: Fichero script para los tests parte III se han construido cuatro tests diferentes que verficarán que el proceso que hemos visto en los diagramas de actividades sea el correcto. Estas pruebas, como hemos visto antes, estarán enfocadas en que se consigan los datos y se procesen correctamente, además de probar que los mensajes de error que deban mostrarse sean los adecuados. 8.2.2 Obtención de datos En esta sección describiremos cada prueba, tanto su objetivo como su código y cómo vamos a verificar si ha sido correcto. PR_FUN_01 Obtiene los datos de la API correctamente de un proyecto privado Objetivo El objetivo de esta prueba es verificar que la aplicación es capaz de obtener los datos de un repositorio existente y privado sin fallos. Descripción Esta prueba llama a las funciones que obtienen y procesan los datos de la API de GitHub. Le pasamos como parámetro el nombre de un proyecto que sabemos que existe, es privado y contiene pull requests. 63 63 Código describe('redux/repos/tests', () => { it(‘Debe obtener los datos del proyecto privado jsdario/yeti’, async () => { //Descripción del test // Creamos un store con datos de ejemplo const store = conFiguraTestStore() // Lanzamos la acción de iniciar sesión de nuestro testUser store.dispatch(actions.userSignedIn(testUser)) // Lanzamos la acción de pedir los datos en este caso es // fetchSinbleRepoDataByLogin(nombreRepositorio) await store.dispatch(thunks.fetchSingleRepoDataByLogin (jsdario/yeti)) // Llamamos al estado de la aplicación const state = store.getState() // Si ha ido bien la petición en el estado de la aplicación la variable erroredRepos debe estar vacía. expect(state.repos.erroredRepos).toEqual([]) // por último se le da un tiempo de ejecución al test, en nuestro caso 10s }, 10000) Código 13: Test que Obtiene los datos de la API correctamente de un proyecto privado Resultado Esperado Se espera que pueda obtener los datos sin problemas, esto se verifica cuando en el estado en la variable de errores no contiene ningún valor. Se hace referencia esta comprobación en la línea del código: expect(state.repos.erroredRepos).toEqual([]) Comentario Para este caso hemos usado un token privado, definido anteriormente, que se obtendrá en el proceso de autorización de la aplicación. PR_FUN_02 Obtiene los datos de la API correctamente de un proyecto público Objetivo El objetivo de esta prueba es verificar que la aplicación es capaz de obtener los datos de un repositorio existente y público sin fallos. Descripción Esta prueba llama a las funciones que obtienen y procesan los datos de la API de GitHub. Le pasamos como parámetro el nombre de un proyecto que sabemos que existe, es público y contiene pull requests. Código El código de este test es el descrito en Código 12: Fichero script para los tests parte III Plan de pruebas 64 Resultado esperado Esta prueba verifica si se pueden obtener los datos sin problemas. Se espera que pueda obtener los datos sin problemas, que en el estado en la variable de errores no tengamos ningún valor. Se hace referencia a este resultado en la línea: expect(state.repos.erroredRepos).toEqual([]) Comentario No es necesario para este test el uso de un token. PR_FUN_03 No obtiene datos y se guarda un error si se introduce un proyecto que no existe Objetivo El objetivo de esta prueba es verificar el caso en el que el usuario introduce en el buscador un nombre de un proyecto que no existe. En este caso el estado de la aplicación debe contener un mensaje de error en la variable correspondiente. Descripción Esta prueba llama a las funciones que obtienen y procesan los datos de la API. Le pasamos como parámetro a la función que obtiene los datos el nombre de un proyecto que sabemos que no existe. Código describe('redux/repos/tests', () => { it(‘Debe guardar un error si el proyecto no existe’, async () => { // Creamos un store con datos de ejemplo const store = conFiguraTestStore() // Lanzamos la acción de iniciar sesión de nuestro testUser store.dispatch(actions.userSignedIn(testUser)) // Lanzamos la acción de pedir los datos en este caso es // fetchSinbleRepoDataByLogin(nombreRepositorio) await store.dispatch(thunks.fetchSingleRepoDataByLogin(‘n oexisto/noExisto’)) // Llamamos al estado de la aplicación const state = store.getState() // Si ha ido bien la petición en el estado de la aplicación la variable erroredRepos no debe estar vacía. expect(state.repos.erroredRepos).toEqual([‘noexisto/noExi sto’]) }, 10000) Código 14: Código del test para un proyecto que no obtiene datos 65 65 Resultado esperado Si el programa ha manejado con éxito el error en la variable erroredRepos debe haberse guardado el nombre del proyecto introducido. Se hace referencia a este resultado en la línea: expect(state.repos.erroredRepos).toEqual([‘noexisto/noExisto’]) Comentario No miramos el tipo de error en este test. Nos basta con comprobar que el programa reconoce que hay un error en este tipo de acción. Si las pruebas se han realizado correctamente al ejecutarlos la salida por pantalla debe ser la siguiente: Figura 39: Resultado de las pruebas unitarias para la obtención de datos del api PR_FUN_04 El proyecto no tiene pull requests que analizar Objetivo Comprobar que el estado local tiene los valores correctos si se intenta analizar un repositorio sin pull requests. Es decir, que almacenamos el mensaje correspondiente al error Descripción Esta prueba llama a las funciones que obtienen y procesan los datos de la API. Le pasamos como parámetro el nombre de un proyecto que existe pero que sabemos que no tiene pull requests, por lo que en el estado se debe guardar error y el mensaje para identificarlo. Las variables encargadas de esto serán erroredRepos y repoErrorsById. Plan de pruebas 66 Código it('Debe guardar el mensage de error correcto cuando no hay p ull requests', async () => { // Creamos un store con datos de ejemplo const store = conFiguraTestStore() // Declaramos una variable para guardar el mensaje que debe almacenar el estado // y así compararlo más tarde. const mensajeError = {"Maluzzz/phpmyadmin": {"message": "Reduce of empty array with no initial value"}} // Lanzamos la acción de iniciar sesión de nuestro testUser store.dispatch(actions.userSignedIn(testUser)) // Lanzamos la acción de pedir los datos en este caso es // fetchSinbleRepoDataByLogin(nombreRepositorio) await store.dispatch(thunks.fetchSingleRepoDataByLogin('Maluzzz/ phpmyadmin')) const state = store.getState() // Si ha ido bien la petición y ha recibido el mensaje del API de GitHub en el estado // de la aplicación la variable erroredRepos no debe estar vacía y repoErrorsById // tampoco expect(state.repos.erroredRepos).toEqual(["Maluzzz/phpmyadmin"]) expect(state.repos.repoErrorsById).toEqual(mensajeError) }, 10000) Código 15: Test para un proyecto que no tiene pull requests que analizar Resultado esperado Se espera que se guarde el nombre del proyecto que tiene error. Además, se debe guardar el mensaje que devuelve la API cuando no hay pull requests, que es: "Reduce of empty array with no initial value" Se hace referencia a este resultado en las líneas: expect(state.repos.erroredRepos).toEqual(["Maluzzz/phpmyadmin"]) expect(state.repos.repoErrorsById).toEqual(mensajeError) Comentario El Proyecto usado para esta prueba ha sido Maluzzz/phpmyadmin 67 67 8.3 Test unitario de interfaz En el fichero Layout.test.js se encuentra la prueba de interfaz. La ejecución de esta prueba permite comprobar que los componentes no tienen variables que vacías o no definidas provocando fallos al cargarse. El código que permite probar la interfaz es el siguiente: // En primer lugar necesitamos los imports de las librerías que usamos de React y Redux import React from 'react' import ReactDOM from 'react-dom' import {Provider} from 'react-redux' // Importamos al componente que instancia todos los demás componentes: Layout import Layout from './Layout' // Y usamos configureStore , visto en el apartado anterior import configureStore from './redux/configureStore' // Comienza la declaración de la prueba con test y el mensaje descriptivo del test test(‘Test: El componente Layout funciona correctamente’, // A partir de aquí es la declaración de la función que probará Layout () => { // Creamos un elemento div para pasarle los parámetros del Store const div = document.createElement('div') // Declaramos la variable objecto store que pasaremos a nuestro componente Provider de Redux const {store} = conFiguraStore() // La siguiente variable será la que pasameos a la función encargada de cargar la interfaz de manera // aislada const testJsx = ( <Provider store={store}> <Layout /> </Provider> ) // ReactDOM.render() instanciará provider, layout y los sucesivos componentes con el store que hemos // cargado anteriormente, si en este proceso algo falla el test nos avisara ya esta prueba está dentro de // test() ReactDOM.render(testJsx, div) }) Código 16: Código del script para la prueba de interfaz La prueba que corresponde con el código visto anteriormente es el siguiente: