El trabajo de código abierto me hace apreciar las pruebas de software. No es un ejercicio académico • El Registro
Enfoque de sistemas Quizás el aspecto más importante de la construcción de sistemas que he llegado a apreciar desde que cambié mi enfoque de las actividades académicas al desarrollo de software de código abierto es la importancia de las pruebas y la automatización de las pruebas.
En el mundo académico, no es exagerado decir que enseñamos a los estudiantes acerca de las pruebas sólo en la medida en que necesitamos casos de prueba para evaluar sus soluciones, y hacemos que nuestros estudiantes graduados ejecuten evaluaciones comparativas de desempeño para recopilar datos cuantitativos para nuestro trabajo de investigación, pero eso es bastante.
Ciertamente hay excepciones –por ejemplo, los planes de estudio centrados en la ingeniería de software–, pero mi experiencia es que la importancia dada a las pruebas en el mundo académico no está alineada con su importancia en la práctica.
Dije que aprecio el papel de las pruebas de software, pero no estoy seguro de entenderlo lo suficientemente clara y profundamente como para explicárselo a alguien más. Como es la naturaleza de nuestra mentalidad de Enfoque de Sistemas, me gustaría entender mejor los «por qué», pero sobre todo lo que veo y oigo es mucha jerga: pruebas unitarias, pruebas de humo, pruebas de inmersión, pruebas de regresión, integración, etc. .
El problema que tengo con esta terminología y otras similares es que es más descriptiva que prescriptiva. Ciertamente, una prueba de integración (por ejemplo) es algo bueno, y puedo ver por qué se podría afirmar razonablemente que una prueba específica es una prueba de integración, pero no estoy seguro de entender por qué es necesaria o suficiente en el gran esquema de las cosas. . . (Si este ejemplo es demasiado oscuro, aquí hay otro ejemplo publicado por un Usuario de Reddit.)
La excepción puede ser las pruebas unitarias, donde la cobertura del código es una métrica cuantificable, pero incluso entonces, mi experiencia es que se le está dando más valor a la capacidad de medir el progreso que a su contribución real a la producción de código de calidad.
Con estos antecedentes, recientemente me encontré tratando de clasificar los más de 700 trabajos de control de calidad (que generan importantes cargos mensuales por parte de AWS) que se han acumulado durante los últimos cinco años en Proyecto Éter.
No creo que la funcionalidad específica sea particularmente importante: Aether consta de cuatro subsistemas basados en microservicios, cada uno implementado como una carga de trabajo de Kubernetes en una nube perimetral, aunque probablemente sea relevante que los subsistemas se administren como proyectos abiertos independientes del código, cada uno con su propio equipo de desarrolladores. Los proyectos, sin embargo, comparten herramientas comunes (por ejemplo, Jenkins) y se alimentan del mismo proceso de CI/CD, lo que lo hace bastante representativo de la práctica de construir sistemas a partir de la integración de múltiples fuentes ascendentes.
Lo que queda claro en mi “estudio de caso” es que existen compensaciones no triviales involucradas, con requisitos en competencia que van en diferentes direcciones. Una es la tensión entre la velocidad de las funciones y la calidad del código, y ahí es donde la automatización de pruebas juega un papel clave: proporcionar las herramientas para ayudar a los equipos de ingeniería a lograr ambas.
La mejor práctica (adoptada por Aether) es la llamada estrategia Shift Left: introducir pruebas lo antes posible en el ciclo de desarrollo (es decir, en el extremo «izquierdo» del proceso de CI/CD). Pero Shift Left es más fácil en teoría que en la práctica porque las pruebas tienen un costo, tanto en tiempo (los desarrolladores esperan que se ejecuten las pruebas) como en recursos (máquinas virtuales y físicas necesarias para ejecutar las pruebas).
¿Qué sucede en la práctica?
En la práctica, lo que he visto es una gran dependencia de que los desarrolladores ejecuten manualmente pruebas funcionales a nivel de componentes. Estas son las pruebas en las que piensa la mayoría de la gente cuando piensa en las pruebas (y cuando publican chistes sobre las pruebas en Reddit), con ingenieros de control de calidad independientes que aportan valor al buscar problemas que los desarrolladores pasan por alto pero que aún no pueden anticipar los casos críticos.
En el caso de Aether, una de las principales pruebas funcionales prueba qué tan bien los desarrolladores han implementado el protocolo 3GPP especificación, una tarea tan compleja que las pruebas comúnmente se compran a un proveedor externo. En cuanto a las pruebas automatizadas, la canalización de CI/CD realiza principalmente pruebas pro forma (por ejemplo, si se compila, si tiene el aviso de derechos de autor apropiado, si el desarrollador firmó el CLA) como puerta de entrada para fusionar un parche en el código base.
Esto impone una pesada carga a las pruebas de integración posteriores a la fusión, donde la cuestión clave es garantizar una “cobertura de configuración” suficiente, es decir, validar que los subsistemas desarrollados independientemente estén configurados de una manera que represente cómo se implementarán como un todo coherente. . La cobertura unitaria es sencilla; la cobertura de todo el sistema no lo es.
Para mí, la idea clave es darme cuenta de que la gestión de la configuración y la eficacia de las pruebas están profundamente entrelazadas. (Esta es también la razón por la que es tan importante automatizar el proceso de CI/CD).
Para hacer esto un poco más tangible, permítanme usar un ejemplo específico de Aether (que no considero único).
Para probar una nueva característica, como la capacidad de realizar múltiples funciones del plan de usuario (UPF), cada uno de los cuales sirve a un segmento diferente de dispositivos inalámbricos: es necesario implementar una combinación de (a) Mobile Core que implementa UPF, (b) Runtime Controller que vincula dispositivos a instancias de UPF y (c) un generador de carga de trabajo que envía un tránsito importante por cada UPF.
Cada uno de los tres componentes viene con su propio «archivo de configuración», que la prueba de integración debe alinear de manera que produzca un resultado completo. En un sistema basado en la nube débilmente acoplado como Aether, la integración equivale a una configuración coordinada.
Ahora imagine hacer esto para cada nueva característica introducida y la cantidad de configuraciones únicas se dispara o descubre cómo hacer que las características adecuadas converjan al decidir selectivamente qué combinaciones probar y cuáles no.
No tengo una buena respuesta sobre cómo hacer esto, pero sé que requiere conocimiento y juicio internos. La experiencia también muestra que muchos errores solo se descubrirán mediante el uso, lo que me dice que las pruebas previas al lanzamiento y la observabilidad posterior al lanzamiento están estrechamente relacionadas. Tratar la gestión de versiones (incluidas las implementaciones por etapas) como una etapa más de la estrategia de prueba es un enfoque holístico sensato.
Volviendo a donde comencé, tratando de comprender las pruebas de software a través de una lente de sistemas, no creo haber cumplido con mis criterios de aceptación personales. Hay varios principios de diseño con los que trabajar, pero la tarea aún se siente como arte e ingeniería a partes iguales.
En cuanto a la proyección que pretendía realizar en el set del trabajo de control de calidad de Aether, todavía estoy luchando por separar el trigo de la paja. Esta es una consecuencia natural de un sistema en evolución sin un plan claro para desactivar las pruebas obsoletas. Esto sigue siendo un trabajo en progreso, pero una conclusión clara es que tanto los estudiantes como los profesionales se beneficiarían si tuvieran una base rigurosa en las pruebas de software. ®