Generación de reportes de cobertura con ReportGenerator

Hace un tiempo escribí sobre medición de cobertura en Net Core utilizando Coverlet. Como indiqué en ese artículo Coverlet tiene la capacidad de medir la cobertura y generar reportes al respecto en distintos formatos. Pero hay una limitación: cuando nuestra cobertura no es del 100% querremos saber puntualmente que métodos/líneas no están cubiertos y si bien Coverlet genera reportes con esta información, los mismos no son en formatos “amistoso para humanos” sino que son en formatos particulares para ser procesados por otras herramientas. Es aquí donde entra en juego ReportGenerator.

ReportGenerator es una herramienta que a partir de un reporte de cobertura en formato “no amistoso para humano” (opencover, cobertura, jacoco, etc) genera reportes en formatos “amistoso para humanos” (csv, html, etc). Esta herramienta está construida en .Net y puede instalarse de varias formas diferentes dependiendo de la plataforma en la que uno pretenda utilizarla. Más aún, la propia herramienta provee una página donde a partir de completar un conjunto de opciones se generan las instrucción de instalación y ejecución correspondientes.

Ahora bien, para poder generar un reporte con ReportGenerator a partir de una medición de cobertura generada con Coverlet hay que indicarle a Coverlet que genere el resultado en un formato que sea compatible con ReportGenerator, por ejemplo formato OpenCover. A continuación comparto un ejemplo.

dotnet test Domain.Tests/Domain.Tests.csproj /p:CollectCoverage=true /p:CoverletOutput=../coverage.info /p:CoverletOutputFormat=opencover

reportgenerator -reports:coverage.info -targetdir:"coverage"

Preparando Ingeniería de Software UNTreF 2020

Ya está definido que este año tendremos que dictar la materia en modalidad completamente a distancia y por ello tendremos que hacer algunos ajustes en la dinámica de las clases y de la materia en general. Al mismo tiempo tenemos ciertas dudas sobre la cantidad de alumnos que cursarán.

Por esto es que hemos habilitado este formulario para llenen que los alumnos interesados en cursar Ingeniería de Software este segundo cuatrimestre de 2020 y de esta forma poder hacer una planificación con menos incertidumbre. Quisiéramos que llenen este formulario todos los alumnos que tengan intención de cursar la materia incluso cuando no reúnan todas las correlativas necesarias.

De paso compartimos aquí algunos detalles de la forma en que dictamos la materia:

  • Intentamos cubrir los temas de la materia con materiales de estudio actualizados y herramientas de uso de frecuente en la industria
  • Para las cuestiones de programación utilizamos Ruby
  • Como herramienta de soporte para las tareas de programación y el trabajo grupal utilizamos GitLab
  • La materia requiere una dedicación semanal de entre 4 y 6 horas adicionales al tiempo de clase
  • La dinámica de evaluación es continua, con tareas semanales que incluyen lecturas, videos, ejercicios de programación y cuestionarios.
  • Este artículo describe formalmente la dinámica de la materia
  • Aquí pueden encontrar varios sobre el dictado de la materia en cuatrimestres anteriores

Enseñanza de Métodos Ágiles en Argentina

Corté el título porque quedaba demasiado largo, pero para ser preciso debería haber puesto: Enseñanza de Métodos ágiles de Desarrollo de Software en Argentina. Estado del Arte. Este es el título formal del trabajo con el que completé mi carrera de Especialista en Tecnología Informática Aplicada en Educación (UNLP).

Este trabajo es un estudio formal y sistemático basado en métodos empíricos de investigación. El trabajo completo tuvo 3 fases.

La primera fase, a fines de 2018, fue incluso antes de comenzar formalmente con el trabajo de especialización. Hice una encuesta entre estudiantes en CONAIISI 2018. El resultado de esa encuesta fue un artículo titulado en “Initial Assessment of Agile Development in theUndergraduate Curricula” que fue publicado en el Workshop Brasilero de Métodos Agiles que se llevó a cabo en el contexto de Agile Brazil 2019.

La segunda fase, ya en el contexto de la especialización, consistió en un mapeo sistemático de literatura que fue publicado en el XXV Congreso Argentino de Ciencias de la Computación con el título Introducing Agile Methods in Undergraduate Curricula, a Systematic Mapping Study

Hasta este punto, toda la información recolectada no resultaba suficiente para tener un entendimiento lo suficientemente certero sobre el estado de la enseñanza de métodos ágiles en Argentina. Entonces ya en la fase 3 realicé una encuesta a docentes de Ingeniería de Software, que es el área donde típicamente se estudian métodos ágiles. Para esto contacte en forma personalizada a los docentes de Ingeniería de Software de la universidades pertenecientes a la Red de Universidades Nacionales con Carreras de Informática, un organismo que agrupa tanto a instituciones públicas como privadas.
Logré así recolectar respuestas de 69 cursos pertenecientes a 44 instituciones distintas. Según los datos provistos por de la Secretaría de Políticas Universitarias del Ministerio de Educación de la Nación , las instituciones relevadas en mi estudio “generaron” en ~77 % de los egresados de las carreras universitarias de informática en el país en el 2017. Esto da cuenta de la representatividad de la muestra analizada.

Entre los resultados encontrados, destacan:

  • Los métodos ágiles son enseñados en el 97.7 % de las carreras de grado relevadas. Más aún, en el 54,5 % de los casos, los métodos ágiles se estudian en el contexto de varias materias que también abordan otros temas.
  • La gran mayoría de los encuestados (83.8 %) dicta sus materias de forma completamente presencial. Este hecho puede estar influido por cuestiones de regulaciones ya que muchas instituciones no admiten alternativas de educación no presencial y exigen asistencia física a las clases. Sin embargo, el 70 % de los encuestados utiliza en sus materias un campus virtual, lo cual representa una extensión del aula y por ende cierto grado de hibridación en la modalidad de enseñanza.
  • El 77 % de los encuestados indicó enseñar Scrum, pero tan solo el 53 % indicó enseñar Retrospectivas, que sin duda es una de las prácticas más importantes de Scrum. Iteration Planning, Iteration Review y Daily Standup son todas prácticas centrales de Scrum y su porcentaje de enseñanza es bastante menor al de Scrum
  • Respecto de las prácticas ágiles; User Stories, Iteration Planning, Planning Pocker, Retrospectives e Iteration Review son las cinco prácticas más enseñadas, las cinco por encima del 50 %. Cabe destacar que estas cinco prácticas pertenecen todas a la categoría “Practicas de gestión”.

Los interesados en leer el trabajo completo lo pueden descargar desde este link.

El próximo lunes 17 a las 19.00 hs voy a hacer un sesión online para compartir los resultados de este estudio y debatir al respecto. Si estas interesado en participar, puedes registrarte aquí.

Y un día no separamos (una historia de crecimiento orgánico)

Hasta aquí…

Comenzamos el proyecto con un equipo completamente nuevo. Según pasaron las iteraciones nos fuimos consolidando como equipo a la vez que sumamos nuevos miembros. De esta forma llegamos a completar la iteración 12 siendo 14 personas. Suficiente, hora de separarnos.

Previamente, allá por la iteración 7, había contado de la previsibilidad de este equipo, un propiedad que el equipo logró mantener a pesar de seguir creciendo. En términos generales, analizando las 12 iteraciones, vemos que la diferencia de trabajo planificado vs trabajo completado no supera el 18%. Esto significado que si este equipo planifica entregar 10 ítems, hay altas probabilidades que al final de la iteración haya completado al menos 8 ítems. El siguiente gráfico muestra el trabajo planificado vs. el trabajo completado para las últimas 3 iteraciones.

Creo que esta característica de previsibilidad es en gran medida consecuencia de un trabajo disciplinado y un crecimiento orgánico.

Algunas métricas de lo realizado hasta aquí:

  • 45 releases a producción
  • 6 meses de trabajo iterativo
  • 382 casos de prueba automatizados que incluyen pruebas unitarias, de integración y aceptación
  • Una cobertura de código superior al 80%

De aquí en más…

Separamos el equipo y separamos el código para permitir que cada equipo sea lo más autónomo posible, pudiendo regular su velocidad delivery sin dependencias. Cada equipo trabajará enfocado en su (sub)producto con sus propios repositorios, infraestructura, microservicio y microfrontend.

Ahora el gran desafío es ver si podemos lograr que ambos equipos mantengan el nivel de efectividad y desempeño logrado hasta el momento.

En un par de meses les cuento.

Adaptación online del Kanban Pizza Game

El Kanban Pizza Game es una actividad tipo simulación, utilizada para enseñar los principios Kanban e implica el trabajo con materiales de librería (papel, tijeras, pegamento, etc). Usualmente utilizamos esta actividad en la clase Lean/Kanban en MeMo2.

Ayer dimos esta clase y ante la imposibilidad de hacerla en forma presencial, debido a la situación de pandemia, decidimos adaptarla para hacerla en forma online. Para ello utilizamos la herramienta Miro.

La actividad salió muy bien, los alumnos dieron feedback muy positivo. Para los interesados aquí publicamos una breve guía de como preparar y facilitar la actividad.

No más cursos de TDD

Estaba escribiendo una respuesta a un tweet en un hilo sobre developer testing y espontáneamente tuve esta revelación: no dictar más cursos de Test-Driven Development.
Si bien no lo tenía listado en mi catálogo de cursos, tenía un curso de TDD que venia dictando en forma privada a pedido de algunas empresas. Pero ya no más. La cuestión es simple: TDD es muy difícil de aplicar en proyectos de complejidad no trivial para gente que recién empieza con TDD y no creo que un curso de 1 o 2 días pueda bastar para que alguien aprenda la técnica y esté en condiciones de aplicarla en un proyecto no trivial apenas terminado el curso. Pero ojo, esto no significa que vaya a dejar de enseñar TDD. No, de ninguna manera. Simplemente voy a cambiar de estrategia: voy compartir gratuitamente un par de videos explicando la técnica (o incluso tal vez recomiende cursos de colegas) y luego ofreceré a los interesados trabajar conjuntamente en su proyecto para aplicar TDD.

Si bien yo uso TDD en mis proyectos, me llevó años y mucha perseverancia poder hacerlo una forma fluida y eficiente. Pero como he dicho más de una vez no considero TDD como una práctica de “efectividad universal”, a mi me resulta eficiente programar haciendo TDD, pero no significa que vaya a resultarle igual a todo el mundo. A mis alumnos les exijo que hagan TDD cuando programan para la materia porque es parte del plan de estudio. A mis compañeros de trabajo les sugiero que hagan TDD si es lo que hemos acordado como equipo, pero dependiendo del contexto me parece completamente válido que agreguen los tests a posteriori si no gustan hacer TDD.

Dicho esto, si quieren aprender TDD conmigo, no me consulten por cursos, contáctenme en privado y coordinemos directamente para hacer un par de sesiones de TDD sobre su proyecto.

DevOops! tal vez igual sirve

Hace un tiempo escribí sobre algunos malentendidos de DevOps en la práctica, hoy quiero compartir algunas otras situaciones a la luz de una definición formal.

De acuerdo a Len Bass y sus colegas del SEI DevOps tiene 5 pilares fundamentales:

  1. Operaciones como ciudadano de primera categoría en el proceso de software delivery
  2. Involucramiento de los desarrolladores en los incidentes productivos
  3. Un proceso formal de deployment
  4. Continuous Delivery
  5. Infrastructure as Code

Pero en la práctica veo que muchas organizaciones que:

  • Pasan completamente por alto los dos primeros puntos, son cuestiones casi “culturales” que no son triviales de cambiar.
  • Van directo al punto 5, incorporan herramientas para la automatización de infraestructura para lo cual contratan algunas nuevas personas, típicamente bajo del rol de “Ingenieros DevOps”. Hacer esto es muy más simple que pretender cambiar una estructura organizacional o un mindset.
  • El punto 4 lo toman solo parcialmente, invierten en la automatización de pipelines de despliegue (tarea que hacen los recientemente incorporados “Ingenieros DevOps”) pero ni noticia de la automatización de pruebas (claro, los DevOps no automatizan pruebas)
  • El punto 3 lo logran como un efecto colateral de la automatización de los despliegues.

El resultado dista bastante de la idea de DevOps de Bass, pero no es tan malo porque a pesar de ello representa una mejora radical respecto de la situación previa.

Continuous Delivery como una cinta transportadora

Hace un par de semanas en un reunión de trabajo mi colega Mariano explicó la práctica de Continuous Delivery como una analogía con una cinta transportadora y me pareció simplemente excelente.

Siendo estrictos con las definiciones Continuous Delivery implica Continuous Integration y Trunk-Based Development. Entonces:

  • El equipo hace commits pequeños, si hace TDD, posiblemente un commit por cada nueva prueba en verde.
  • Un funcionalidad/story requiere de varios commits.
  • Si el equipo trabaja simultáneamente en más de una funcionalidad es común que una funcionalidad se complete mientras otra está aún en desarrollo.
  • Si efectivamente hacen continuous delivery es posible que a penas se complete una funcionalidad se la quiera poner producción. Esto a su vez implicaría llevar a producción también las funcionalidades aún no completas.

De esta forma el proceso de continuous delivery es una cinta transportadora donde cada commit es como un paquete que uno pone en la cinta y la cinta lo lleva a producción.

Esto tiene un impacto muy grande en la forma del trabajo del equipo que obliga a tomar precauciones adicionales como puede ser el uso de feature toggles, de manera que si una funcionalidad no completa llega a producción, la misma pueda ser “toggleada/apagada”.

Medición de cobertura en .Net Core con Coverlet

Luego de cumplir con los primeros hitos de negocio y teniendo un equipo que empieza a estabilizarse me puse a hacer algunas pruebas para medir la cobertura de nuestro proyecto.

En primera instancia atiné a utilizar OpenCover, una herramienta que había utilizado en proyectos anteriores, pero me encontré que solo corre en Windows. Nuestra infraestructura de build corre en Linux y yo particularmente trabajo en MacOS. Con lo cual OpenCover quedó descartado.

Luego de Googlear un poco dí con Coverlet que según la documentación es multiplataforma. Investigando un poco más encontré este artículo de Scott Hanselman y con eso me bastó para hacer una prueba. A continuación voy a compartir algunos descubrimiento que hice aprendiendo a utilizar esta herramienta.

En primer lugar tenemos que saber que hay tres formas de utilizar esta Coverlet:

  1. Como una extensión de dotnet-cli
  2. Como un collector integrado al motor de ejecución de VSTest
  3. Como una tarea de MSBuild

Yo decidí ir por esta última estrategia. Para ello el primer paso es agregar el paquete coverlet.msbuild a cada uno de los proyectos de tests. Una vez agregado este paquete simplemente tenemos que agregar el parámetro de cobertura al momento de la ejecución de los tests

dotnet add package coverlet.msbuild
dotnet test /p:CollectCoverage=true

Si tenemos un solo proyecto de tests que cubre todos los proyectos/assemblies de nuestra solución, con esto ya habrá sido suficiente. Al ejecutar los comandos anteriores obtendremos una salida como la que se muestra en la siguiente figura.

Pero si nuestra solución, como es muy habitual, tiene varios proyectos de tests nos vamos a encontrar que la medición que hace Coverlet es “parcial”/”desagregada”. Ocurre que cada proyecto de tests es ejecutado ejecutado independientemente haciendo que la medición de cobertura también sea independiente lo cual a su vez hace que tengamos varios reportes de cobertura. Esto se puede observar en la siguiente figura.

El primer reporte corresponde al proyecto de tests de Domain y nos indica que precisamente Domain tiene una cobertura de ~79%.
El segundo reporte corresponde al proyecto de tests de ApiServices y como ApiServices depende de Domain, la medición de cobertura considera ambos proyectos. Pero dado que los tests de ApiServices apenas tocan el código de Domain, la cobertura informada sobre Domain es mínima (~6%). Entonces lo que deberíamos hacer para obtener el valor correcto de cobertura es mezclar el reporte de cobertura generado por el proyecto de tests de Domain y el proyecto de tests de ApiServices. Aquí también coverlet nos da varias opciones. En mi caso, lo que hice fue ejecutar explícitamente cada proyecto de test por separado, escribiendo los resultados en un archivo y en la misma ejecución indicándole a Coverlet que realice el merge con el archivo de cobertura de la ejecución anterior.

dotnet test Domain.Tests/Domain.Tests.csproj /p:CollectCoverage=true /p:CoverletOutput=../coverage.json

dotnet test ApiServices.Tests/ApiServices.Tests.csproj /p:CollectCoverage=true /p:CoverletOutput=../coverage.json /p:MergeWith=../coverage.json

De esta forma el reporte se genera correctamente.

Una situación habitual cuando medimos la cobertura es no incluir en el cálculo algunas archivos. En nuestro caso ocurre que consumimos servicios SOAP, y utilizamos una herramienta que nos genera un conjunto de clases proxy para interactuar con SOAP. Dichas clases son almacenadas en un archivo Reference.cs que queremos excluir del análisis de cobertura. Para esto debemos incluir un parámetro adicional para Coverlet en la ejecución de los test /p:ExcludeByFile=”**/Reference.cs”.

Bien, con todo lo descripto hasta el momento hemos logrado medir el % de cobertura de nuestro código. Lo siguiente que suele hacerse es ver cuales son las partes de código sin cobertura. Si bien coverlet tiene la capacidad de detectar esto, no provee un mecanismo cómodo para visualizarlo y por ello debemos utilizar otra herramienta complementaria. Pero eso será parte de otro post.

Sobre la secuencia de tests al hacer TDD

Este es uno de los temas clave al hacer TDD. La elección de la secuencia de test puede hacer que el ciclo de TDD resulte muy fluido o que sea una pesadilla. Más aún, tengo la sospecha que la elección inapropiada de la secuencia de tests debe ser una de las principales razones de abandono de TDD. Personalmente suelo aplicar la heurística conocida como “TDD Guided by Zombies” propuesta por James Grenning, pero incluso usando esa heurística hay situaciones en la que mi secuencia de tests no me resulta lo suficientemente fluida. Quiero compartir un caso concreto para repasar distintas heurísticas.

Ayer por la tarde estábamos con otros 3 colegas en un sesión de mobbing trabajando sobre una funcionalidad que consistía en filtrar una lista de objetos Cuenta en base a 4 condiciones. En caso de no encontrar ningún objeto que cumpla con las 4 condiciones en forma simultánea debíamos generar un error pues eso representaba una situación excepcional de negocio.

Un colega propuso comenzar por los casos de excepción y luego los casos positivos. Lo que me gusta de esta heurística es que inicialmente permite avanzar rápido, el código del método generado comienza con una secuencia de validaciones que van respondiendo a los distintos casos de excepción. Lo que no gusta de esta secuencia es que durante varios ciclos el código no resuelve el problema para nadie, o sea, el método en cuestión solo valida y lanza excepciones pero no genera una lista filtrada. Hay que esperar casi hasta el final de la secuencia para obtener una lista filtrada. Una posible secuencia de tests con esta estrategia sería:

DeberiaLanzarExcepcionSiNoTieneCuentasEnPesos
DeberiaLanzarExcepcionSiNoTieneCuentasConTarjetaAsociada
DeberiaLanzarExcepcionSiNoTieneCuentasTitular
DeberiaLanzarExcepcionSiNoTieneCuentasActivas
DeberiaFiltrarLasCuentasEnPesosConTarjetaTitularidadActivas
...(otros casos positivos)...

Personalmente yo prefiero comenzar con casos positivos y dejar las situaciones de excepción para el final. En este sentido un camino posible sería arrancar literalmente al revés del ejemplo anterior, comenzando con el caso positivo que contempla todas las condiciones. El problema de esta estrategia es que genera un paso muy grande, o sea, implica escribir en un solo paso el algoritmo completo que considera las 4 condiciones.

Otra opción en línea con la idea de transitar el camino de casos positivos y siguiendo la heurística de Zombies, es arrancar con una condición positiva a la vez. De esta forma, ya de entrada estamos generando una lista filtrada, que al menos para algunos casos será una lista completamente válida. A partir de eso podemos ir generar nuevos casos positivos refinando el filtro y agregando los casos de excepción luego de tener todo el filtro completo. De esta forma cada paso del ciclo se mantiene pequeño y el algoritmo de filtrado se construye incrementalmente. La secuencia resultante podría ser:

DeberiaFiltrarSoloCuentasEnPesos
DeberiaFiltrarSoloCuentasConTarjeAsociada
DeberiaFiltrarSoloCuentasTitular
DeberiaFiltrarSoloCuentasActivas
.... (otros casos incluyendo los de excepcion)....

Obviamente que también está la posibilidad de tomar una secuencia que alterne casos positivos y negativos. De hecho si uno siguiera a raja tabla la heurística Zombie el primer caso (caso Zero) sería negativo y los siguientes positivos (caso One y casos Many).

Bueno, esto esto por ahora pero creo que este tema de la elección de la secuencia de tests aún da para mucha charla.