Escribir suites de pruebas es una habilidad que es una parte vital del entrenamiento de los programadores. Aprender a escribir buenas pruebas le ayuda a escribir un código más robusto y asegura que cuando haya escrito un código que funcione, siga trabajando durante mucho tiempo en el futuro. También puede ayudarle a escribir mejor código en primer lugar. Resulta que el código bien diseñado, con alta cohesión y bajo acoplamiento, también es fácil de probar - por lo que escribir código que sea fácil de probar casi siempre dará como resultado una mejor calidad total del código.

Un paso importante en la "nivelación" de su experiencia de pruebas es comenzar a usar un servicio de integración continua o CI. Un servicio de CI es una herramienta que ejecuta automáticamente su suite de pruebas cada vez que alguien realiza un cambio o cada vez que alguien propone un cambio en forma de una solicitud de PR. El uso de un servicio de CI garantiza que su código siempre pase su suite de pruebas - no puede introducirse accidentalmente en un cambio que rompe una prueba, porque obtendrá una notificación de advertencia roja grande.

Un CI es un servicio tan importante que muchas empresas sólo existen para proporcionar CI-as-a-service. El proyecto BeeWare ha utilizado, para diversos proyectos, TravisCI y CircleCI. Ambas herramientas proporcionan niveles gratuitos para proyectos de código abierto y han patrocinado generosamente BeeWare con actualizaciones de capacidad en varias ocasiones.

Sin embargo, BeeWare ha tenido una relación interesante con los servicios comerciales de CI. Esto es por dos razones.

En primer lugar, nuestros conjuntos de pruebas, especialmente para VOC y Batavia - toman mucho tiempo para ejecutarse. Estos dos proyectos requieren pruebas que inician y cierran repetidamente las máquinas virtuales (para Java y JavaScript, respectivamente), y no importa cuánto se optimice el código que se está probando, el tiempo de inicio y apagado de una máquina virtual eventualmente se acumula. También necesitamos ejecutar nuestras suites de prueba en múltiples versiones de Python - en la actualidad, soportamos Python 3.4, 3.5 y 3.6, con 3.7 en el horizonte. También hay cambios sutiles en versiones micro que pueden requerir pruebas.

Hemos sido capaces de acelerar las prueba, dividiendo la suite de pruebas y ejecutando partes de la suite en paralelo, pero eso nos obliga a enfrentarnos al segundo problema. Los servicios comerciales de CI suelen operar en un modelo de suscripción; mayores suscripciones que proporcionan más recursos simultáneos. Sin embargo, nuestro patrón de uso es muy inusual. La mayor parte del año, recibimos un goteo lento de solicitudes de PRs que requieren pruebas. Sin embargo, un par de veces al año, tenemos un gran sprint, y tenemos una avalancha de contribuciones durante un corto período de tiempo. En PyCon EE.UU., hemos tenido grupos de 40 personas presentando parches - y todos ellos necesitan sus PRs probados por CI. Y el tiempo es un factor - los sprints sólo duran un par de días, por lo que un rápido cambio en las pruebas es esencial.

Si tuviéramos que suscribir a los niveles de suscripción de nivel superior de CircleCi y TravisCI, todavía no tendríamos suficientes recursos para soportar un sprint - pero estaríamos masivamente sobre abastecidos en recursos durante el resto del año. También tendríamos que pagar $ 750 o más por mes por este servicio, que es un presupuesto que no podemos permitirnos.

Así que tuvimos un problema. Para ejecutar nuestra suite de pruebas de manera eficaz, necesitábamos recursos paralelizados masivamente para ejecutar una suite de pruebas rápidamente; y en ciertas épocas del año, necesitamos un número extremadamente grande de estos recursos.

También teníamos otras tareas automatizadas que queríamos realizar. Queríamos hacer linting del código (comprobación automatizada de estilo de código) antes de que se probara un PR. Queríamos verificar la ortografía de la documentación. Y queríamos que estas tareas se retroalimentaran en GitHub como comentarios automatizados y marcadores de estado de revisión de código específicos, informando a los colaboradores no sólo de que se había producido un problema, sino de qué problema y dónde estaban en su código.

También queríamos administrar las compilaciones de pipeline - no tiene sentido hacer una prueba completa de varias versiones de Python una vez que haya establecido que las pruebas están fallando en una versión. Y no hay punto en hacer pruebas en absoluto si hay problemas de estilo de código.

También queríamos hacer cosas que no eran sólo pruebas. Queríamos verificar que se firmaron acuerdos de contribución. Queríamos automatizar el despliegue de sitios web y documentación.

Lo que teníamos no era sólo un problema de CI. Era un problema donde queríamos ejecutar automáticamente código arbitrario, de manera segura, en respuesta a un evento GitHub.

He estado tratando de encontrar un servicio de CI que pueda satisfacer nuestras necesidades durante más de un año. Pero durante el último año, algunos pensamientos comenzaron a congelarse en mi cabeza.

  • Amazon proporciona una API (EC2) que le permite arrancar máquinas de complejidad variable (hasta 64 CPUs, con casi 500 GB de RAM) y pagar por minuto para ese uso.
  • Docker proporciona las herramientas para configurar, lanzar y ejecutar código de manera aislada
  • Amazon también proporciona una API (ECS) para controlar la ejecución de los contenedores Docker.

No hay nada específico acerca de AWS EC2 o ECS tampoco - se podría utilizar Linode y Kubernetes, o Docker Swarm o Microsoft Azure ... simplemente se necesita tener la capacidad de filtrar fácilmente las máquinas y ejecutar un contenedor Docker. Después de todo: un conjunto de pruebas es sólo un contenedor Docker que ejecuta un script que inicia su suite de pruebas. Una revisión de linting es un contenedor de Docker que ejecuta un script que lints su código. Una verificación de acuerdo de contribuyente es un contenedor de Docker que comprueba los metadatos asociados con una solicitud de extracción.

Todo lo que necesita es un sitio web que puede recibir las notificaciones de eventos de GitHub e iniciar los contenedores de Docker en respuesta.

A principios de julio, me encontré entre trabajos, y pronuncié la fatídica pregunta: "¿Cuán difícil podría ser?" Y así, hoy, estoy anunciando BeeKeeper - el propio servicio de CI de BeeWare.

BeeKeeper se despliega como un sitio web de Heroku, escrito con Django. Después de configurarlo con las credenciales de Github y AWS, escucha los webhooks de Github. Cuando se detecta una solicitud de pull o Push, BeeKeeper crea una solicitud de generación; que la solicitud de construcción inspecciona el código en el repositorio en busca de un archivo de configuración beekeeper.yml. Ese archivo de configuración describe el pipeline de tareas que se van a realizar, y para cada tarea, el tipo de máquina que se debe usar, las variables de entorno que se requieran y la imagen de Docker que se utilizará.

BeeKeeper también permite al administrador del sitio describir qué recursos se utilizarán para satisfacer las compilaciones. Una tarea puede decir que necesita una instancia de "Alta CPU"; pero la instancia BeeKeeper puede determinar lo que significa "alta CPU" - ¿es 4 CPUs o 32? ¿Cuándo esas máquinas son coladas hacia arriba, cuánto tiempo se les permitirá sentarse inactivo antes de ser apagado de nuevo? ¿Cuántas máquinas deben estar permanentemente en el grupo de trabajo? ¿Y cuál es el límite superior de las máquinas que se iniciará?

Una herramienta complementaria para BeeKeeper es Waggle. Waggle es una herramienta que prepara una definición local de una tarea para que pueda ser utilizada por BeeKeeper - compila la imagen de Docker y la carga en ECS para que pueda ser referenciada por tareas. (Se llama "Waggle" porque cuando las abejas obreras descubren una buena fuente de néctar, regresan a la colmena y hacen una 'danza' que le dice a otras abejas cómo hacer para encontrar esa fuente).

También hemos proporcionado un repositorio llamado Comb (nombrado después del peine de miel, las abejas del lugar almacenan todo el néctar que encuentran) que define las configuraciones de la tarea que una instancia de BeeKeeper puedo usar. Hemos proporcionado algunas definiciones simples como parte del repositorio base de Comb; cada implementación de BeeKeeper debe tener uno de estos repositorios propios.

Todavía hay mucho trabajo por hacer, pero ya estamos usando BeeKeeper con Batavia y VOC, y la próxima PyCon AU sprints será nuestra primera en condiciones de alta carga. Algunos cálculos de respaldo prevén que por alrededor de $50, podremos proporcionar suficientes recursos de CPU para cada ejecución de prueba para completar su ejecución en 10 minutos o menos, soportando un sprint de decenas de personas.

Aunque BeeKeeper fue escrito para satisfacer las necesidades del proyecto BeeWare, es una herramienta de código abierto disponible para cualquier persona. Si desea tomar BeeKeeper para darle una prueba, vaya a los sprints, o revise el código en GitHub.

BeeKeeper también es un ejemplo del tipo de producto que vería más si el desarrollo de BeeWare se financiara a tiempo completo. Fui capaz de construir BeeKeeper porque tenía un par de semanas de descanso entre los trabajos. No hay fin para las herramientas y bibliotecas como BeeKeeper y Waggle que podrían ser construidas para soportar el proceso de desarrollo de software - todo lo que falta son los recursos necesarios para desarrollar esas herramientas. Si desea ver más herramientas como BeeKeeper en el mundo, considere la posibilidad de unirse al proyecto BeeWare como miembro financiero. Cada pedacito ayuda, y si podemos alcanzar a una masa crítica de patrocinadores, podré comenzar a trabajar en BeeWare a tiempo completo.