Moodle · Ingeniería

Una prueba que no puede fallar no está probando nada

Distingo tres niveles: pruebas unitarias de la lógica propia, pruebas de integración con Moodle y pruebas funcionales mediante la interfaz. Cubro los permisos, los datos inválidos y los fallos de sistemas externos en el nivel más barato que permita comprobar el comportamiento completo. Que una batería nunca haya fallado no demuestra que proteja: introduzco un fallo controlado y compruebo que lo detecta.

  • Las pruebas unitarias cubren la lógica propia, las de integración comprueban el encaje con Moodle y las funcionales recorren la interfaz como una persona.
  • Una prueba solo vale si falla cuando se rompe lo que dice comprobar.
  • Lo que más ahorra a largo plazo es probar los casos límite y los permisos, no solo el recorrido previsto.
  • Sin pruebas, adaptar una extensión a una versión nueva puede acercarse al coste de reescribir partes relevantes, porque cada cambio exige una comprobación manual amplia.

Experiencia en ingeniería

Pruebo la lógica propia con pruebas unitarias, la integración con la plataforma y los recorridos críticos a través de la interfaz. Incluyo permisos, casos de error y resultados observables, y elimino dependencias entre pruebas, fallos inestables y comprobaciones atadas a la maquetación.

Mi trayectoria

Cuándo se echan en falta

Cuatro momentos en los que la ausencia de pruebas acaba costando dinero.

01

En cada actualización de versión

Sin pruebas, he tenido que comprobarlo todo a mano: la actualización exige semanas de trabajo y acaba aplazándose un año más.

02

Cuando alguien toca código que no escribió

Sin una red de pruebas que avise de los fallos, he visto equipos cambiar con miedo o renunciar a los cambios. Ese miedo deja funcionalidades congeladas durante años.

03

Qué reviso al recibir la entrega de un proveedor

Sin pruebas automatizadas, dependo de una revisión manual amplia y me cuesta más repetirla después de cada cambio.

04

Cuando reaparece un fallo ya corregido

He visto reaparecer un fallo ocho meses después de corregirlo. Sin una prueba que comprobara que seguía corregido, nada impedía que volviera.

Qué compruebo con cada mecanismo de prueba

Organizo las pruebas en tres niveles. Sitúo los permisos, los datos inválidos y los fallos externos en el nivel más barato que me permite comprobar el comportamiento completo.

  1. 01

    Pruebas unitarias de la lógica propia

    Con pruebas unitarias compruebo funciones y clases con datos concretos, sin interfaz. Son rápidas, estables y baratas, así que concentro en ellas el grueso de las pruebas: cálculos, reglas de negocio, transformaciones y casos límite.

  2. 02

    Pruebas de integración con la plataforma

    Compruebo que la extensión funciona bien con la base de datos, los eventos y las interfaces del núcleo. Estas pruebas son más lentas, pero detectan si un cambio de versión ha roto algo.

  3. 03

    Pruebas funcionales a través de la interfaz

    Con estas pruebas simulo el recorrido de una persona por la plataforma. Escribirlas y mantenerlas tiene un coste alto, así que las reservo para los recorridos que no pueden fallar: acceder, hacer un cuestionario, calificar y matricularse.

Cómo decido qué probar

Me hago cinco preguntas para evitar una batería de pruebas grande e inútil.

  1. 01

    ¿Esta prueba puede fallar alguna vez?

    Introduzco un fallo controlado en el comportamiento exacto que la prueba afirma proteger. Si entonces sigue en verde, la prueba no está comprobando ese comportamiento.

  2. 02

    ¿Esto lo prueba ya la plataforma?

    No duplico las pruebas internas del núcleo. Desde la extensión sí pruebo las interfaces y los comportamientos de Moodle que la propia extensión necesita para funcionar.

  3. 03

    ¿Qué pasa si esto falla en producción?

    Siempre pruebo lo que afecta a calificaciones, accesos o datos. Casi nunca pruebo lo cosmético.

  4. 04

    ¿He probado los permisos?

    Compruebo que alguien sin derechos no puede hacer la operación y que quien los tiene sí puede. Ambas pruebas son igual de importantes, pero la negativa suele faltar.

  5. 05

    ¿Quién corrige una prueba que falla?

    Asigno esa responsabilidad a una persona concreta. Si no lo hago, las pruebas inestables se desactivan una a una hasta que la suite deja de significar algo.

Diez comprobaciones sobre vuestras pruebas

La primera señal es la que más veces me ha revelado que una batería de pruebas no sirve.

  • Si rompo el código a propósito, alguna prueba falla.
  • Las pruebas se pueden ejecutar sin depender de quien las escribió.
  • Cada prueba prepara y limpia sus propios datos.
  • El orden de ejecución no altera el resultado.
  • Se prueban los casos de error, no solo el camino correcto.
  • Se comprueba que quien no tiene permisos no puede operar.
  • Ninguna prueba falla de vez en cuando sin causa real.
  • La suite tarda lo bastante poco como para lanzarla a menudo.
  • Las pruebas solo dependen de textos exactos cuando el texto forma parte del comportamiento, la accesibilidad o el requisito que se quiere proteger; para localizar elementos se usan selectores estables.
  • Hay alguien responsable de arreglar una prueba rota el mismo día.

Lo que echa a perder una suite

Seis problemas que detecto cuando las pruebas cuestan más de lo que aportan.

Pruebas que no comprueban resultados

Descarto las pruebas que ejecutan el código sin verificar nada o que solo verifican algo trivial: dan cobertura, pero ninguna protección.

Pruebas inestables que fallan sin motivo

He visto cómo el equipo deja de prestar atención a los fallos en cuanto una batería de pruebas falla de forma intermitente sin que exista un defecto real. A partir de ahí, la batería ya no sirve.

Dependencia del estado de otra prueba

Preparo y limpio los datos de cada prueba para que el orden de ejecución no importe. Así, un cambio no rompe pruebas sin relación.

Pruebas atadas a la maquetación

Evito verificar textos exactos y rutas frágiles de la interfaz, porque un cambio visual puede romper pruebas que no guardan relación con él.

Sin pruebas de los casos de error

Si pruebo solo los casos que van bien, cubro justo los que menos fallan en producción. Empiezo por las excepciones.

Una ejecución tan lenta que nadie la inicia

Si la suite completa tarda una hora, solo la ejecuto el día del despliegue, no cuando todavía sirve de algo.

Qué cambia con el tamaño del desarrollo

Ajusto la inversión a lo que hay en juego si algo se rompe.

  1. Extensión pequeña

    Empiezo por pruebas unitarias sobre la lógica propia. Añado pruebas por interfaz cuando la extensión cambia un recorrido crítico, depende mucho de JavaScript o un fallo tendría consecuencias relevantes; el tamaño del código no basta para descartarlas.

  2. Varias extensiones relacionadas

    Añado pruebas de integración: detectan los fallos en la interacción entre piezas que no aparecen al probar cada pieza por separado.

  3. Desarrollo continuo

    Ejecuto las pruebas automáticamente con cada cambio y corrijo cualquier inestabilidad el mismo día. Sin esa disciplina, el conjunto de pruebas se degrada en cuestión de meses.

  4. Antes de una actualización de versión

    Entonces las pruebas acotan con rapidez qué comportamientos cubiertos han fallado, en lugar de obligarme a descubrirlos todos mediante comprobaciones manuales.

Qué exigir sobre pruebas en un contrato

Fijo seis requisitos para evitar una batería de pruebas decorativa.

RequisitoEvidencia exigibleCriterio de aceptación
Pruebas de la lógica propiaBatería de pruebas ejecutable con la herramienta oficial de la plataformaLa organización ejecuta todas las pruebas sin ayuda y obtiene un resultado correcto en todas
Pruebas de integración con MoodleCasos que prueban la base de datos, los eventos y las interfaces utilizadasEjecutar los casos acordados en cada versión objetivo y detectar los fallos de integración
Pruebas de los recorridos críticosPruebas funcionales de los flujos acordadosCubrir los recorridos acordados y ejecutar sus pruebas en la entrega
Pruebas de permisosCasos de prueba con un rol sin autorizaciónComprobar que la operación también se deniega, no solo que se permite
Pruebas que detectan roturasDemostración: las pruebas fallan después de alterar el código a propósitoCada prueba falla cuando se rompe lo que comprueba
Ejecución reproducibleInstrucciones y entorno para ejecutarlasLa organización ejecuta por su cuenta el conjunto completo de pruebas

Cuánto probar

Distingo tres niveles de inversión. Elegir un nivel demasiado alto sale tan caro como elegir uno demasiado bajo.

01

Solo la lógica propia

Escribo pruebas unitarias para las funciones que contienen las reglas de la organización, sin tocar la interfaz.

A favor

  • Barato de escribir y muy rápido de ejecutar
  • Estable: no falla por motivos ajenos
  • Protege justo lo que nadie más prueba

En contra

  • No detecta problemas de integración ni de interfaz
  • No cubre los recorridos completos

Cuándo tiene sentido Extensiones pequeñas con lógica propia clara. Es el mínimo razonable para cualquier desarrollo.

02

Lógica, integración y recorridos críticos

Combino lo anterior con pruebas de integración con Moodle y con un conjunto delimitado de pruebas a través de la interfaz sobre lo que no puede fallar.

A favor

  • Cubre lo caro sin disparar el coste
  • Detecta roturas en actualizaciones
  • Permite refactorizar con confianza

En contra

  • Las pruebas por interfaz exigen más mantenimiento
  • La ejecución completa deja de ser instantánea

Cuándo tiene sentido Es el equilibrio que recomiendo para casi cualquier desarrollo en producción.

03

Suite completa con ejecución continua

Añado a lo anterior una cobertura amplia, ejecuto las pruebas automáticamente con cada cambio y pruebo el rendimiento de lo nuevo.

A favor

  • Permite entregar a menudo con poco riesgo
  • Los fallos se detectan en minutos

En contra

  • Coste de construcción y de mantenimiento alto
  • Necesita infraestructura de ejecución
  • Se degrada si nadie repara las pruebas inestables

Cuándo tiene sentido Desarrollo continuo con equipo dedicado y plataforma crítica. No para una extensión que se toca dos veces al año.

Tres prácticas que dan más sensación de seguridad que cobertura real

Las métricas pueden parecer buenas, pero dejo huecos importantes si persigo el porcentaje de cobertura, pruebo solo el recorrido previsto o ejecuto toda la batería de pruebas desde la interfaz.

  • Perseguir un porcentaje de cobertura

    Cubrir líneas no me demuestra que el comportamiento sea correcto. Verifico los resultados de la función, porque una prueba puede ejecutar todas sus líneas sin comprobar ninguno.

  • Probar solo el recorrido previsto

    Con datos correctos y una persona con permisos, todo va bien. Pruebo también los demás casos: si solo sigo el recorrido previsto, no los cubro y no veo sus fallos.

  • Automatizar todas las pruebas desde la interfaz

    Descarto automatizar cada pantalla. Las pruebas por interfaz son lentas y frágiles, y una batería completa tarda horas, da fallos por motivos ajenos y acaba desactivada.

Referencias

Fuentes y límites

La proporción recomendada entre capas y el criterio de qué merece la pena automatizar son propios, formados escribiendo y manteniendo suites concretas: no proceden de una recomendación oficial. Las herramientas y su configuración están en la documentación enlazada y cambian entre versiones. La comprobación de que una prueba falla al romper el código a propósito es una práctica conocida en ingeniería de software; aquí la presento porque es la que más veces destapa suites que no protegen.

Blog

Casos, pruebas y fuentes.

En el blog desarrollo casos particulares, pruebas y fuentes que amplían esta página de referencia.

Ir a blog.albertolarah.com

Contacto

Cuando hay pruebas y cada cambio sigue dando miedo

Suele significar que la batería no comprueba lo que hace falta, o que se ha vuelto inestable. Reviso qué comprueba, cuánto tarda en ejecutarse, con qué frecuencia falla sin causa aparente y qué pasó la última vez que algo se rompió en producción.

hola@albertolarah.comLinkedIn ↗