BookinglyTech News
Software

DeepCover mide si tus tests verifican el código, no si lo ejecutan

El autor de la herramienta borró una condición en la librería radashi y las 911 pruebas siguieron en verde con la cobertura al 100%. La métrica no puede distinguir eso.

3 min de lecturaDev.to0 vistas

El autor de DeepCover eliminó una condición de un guard en radashi, una librería TypeScript de utilidades que bloquea el merge si no hay 100% de cobertura de líneas y ramas, y las 911 pruebas de la suite siguieron pasando. La cobertura siguió marcando 100%, incluido el fichero que acababa de tocar. El problema no es de radashi: es de la métrica.

El cambio fue mínimo. El guard if (!arrays || !arrays.length) pasó a ser if (!arrays.length). Con pnpm test, que es el comando real de su CI, todo en verde. La suite es mejor que la mayoría: cuando el autor puso if (false) en un guard de replaceOrAppend, un test falló. El arnés mata mutantes, solo que no mata ese.

Qué mide DeepCover

La cobertura cuenta cuántas veces se evalúa un operando, nunca si ese operando decidió algo. Si todos los tests pasan arrays definidos, puedes borrar la comprobación de nulos y la nota no se mueve. El proveedor v8 registra que la rama se ejecutó; no registra si !arrays hizo algún trabajo.

DeepCover, que el autor escribió y publicó en GitHub, intenta puntuar si los tests verifican el código, no si lo ejecutaron. Funciona en tres fases: extrae un modelo del módulo (ramas con operandos || y && separados, cada test mapeado a su función, grafo de dependencias, cobertura por función); razona sobre ese modelo con un agente que devuelve JSON tipado; y analiza con una puntuación compuesta y subpuntuaciones. La influencia del LLM está limitada a ±20% por subpuntuación.

Sobre src/array de radashi —35 funciones, 911 tests, 100% de cobertura— el resultado es 89/100 compuesto, 100 en calidad de aserciones y 63 en cobertura de estados: 37 de 112 estados de dominio sin ningún test. Un estado de dominio es una situación que la lógica de la función puede adoptar (entrada vacía o no vacía, un filtro que lo elimina todo, un matcher propio), no una línea ejecutada. Los tests que hay están bien escritos; lo que falta es un tercio de los escenarios.

El autor metió un throw en algunos huecos y la suite siguió verde: en iterate con count <= 0 y en select cuando una entrada no vacía queda filtrada a nada. También encontró un fallo real: toggle y selectFirst usan Array.prototype.find, que devuelve undefined tanto si no hay coincidencia como si el elemento que coincide es undefined. Abrió las issues 482 y 483 en el repositorio.

Límites

El autor lo dice sin adornos: la puntuación completa usa un agente como razonador, no es un linter que se deje en CI sin supervisión (con --no-llm solo hace la parte determinista). La pasada determinista de bugs es ruidosa a propósito y la de razonamiento los valida; en esta ejecución rechazó los siete como falsos positivos. Tampoco había datos por operando (el proveedor v8 de Vitest 2), así que esa comprobación quedó desactivada en vez de estimarse. La condición borrada del principio fue una edición a mano contra la suite, no una función de mutación.

Se lanza con npx @anatolykhelmer/deep-cover@0.10.1 run --root . --module src/foo.

Que una suite con umbral del 100% tenga condiciones muertas y estados sin cubrir es un recordatorio incómodo para cualquiera que gatee merges con esa cifra. Lo que no está claro todavía es si una herramienta con un LLM dentro puede convertirse en un paso más del pipeline sin supervisión, o si su sitio es la revisión puntual cuando alguien sospecha que su cobertura miente.