Ir al contenido

Tutorial: Medir la cobertura de las pruebas del front

Al terminar este tutorial vas a ser capaz de:

  • Instalar el paquete que mide la cobertura y correr las pruebas con ella.
  • Leer la tabla de cobertura y encontrar las líneas que ninguna prueba ejecuta.
  • Ver esas líneas marcadas en el reporte del navegador.
  • Escribir la prueba que falta y comprobar que el número sube.
  • Explicar por qué una prueba puede subir la cobertura sin proteger nada.
  • Saber qué hace el pipeline del grupo con ese reporte.

Prerrequisito: el tutorial de pruebas del front con Vitest, porque aquí se mide la cobertura de esas pruebas. La teoría está en Pruebas del front con Vitest.


La cobertura cuenta qué parte del código se ejecutó mientras corrían las pruebas. No mide si lo ejecutado se verificó: eso lo muestra la sección 7. Vitest la reporta con cuatro columnas:

Columna Qué cuenta
% Stmts Las sentencias (cada instrucción) que se ejecutaron
% Branch Las ramas de los if, los ?: y los && que se tomaron
% Funcs Las funciones que se llamaron al menos una vez
% Lines Las líneas que se ejecutaron

Una última columna, Uncovered Line #s, lista las líneas que ninguna prueba ejecutó. Es la más útil.

Antes de empezar, corre las pruebas: deben terminar en verde.

Ventana de terminal
pnpm test --watch=false

Vitest no trae el medidor. Si corres con cobertura sin instalarlo, Angular lo avisa y no corre nada:

The following packages are required but were not found:
- Code coverage requires either "@vitest/coverage-v8" or "@vitest/coverage-istanbul" to be installed.

El paquete debe tener exactamente la misma versión que vitest. Mira cuál tienes:

Ventana de terminal
pnpm list vitest

Y instala el medidor con esa versión (en el ejemplo, 4.1.11):

Ventana de terminal
pnpm add -D @vitest/coverage-v8@4.1.11

Ventana de terminal
pnpm test --watch=false --coverage --coverage-reporters=lcov --coverage-reporters=text

El reporte text sale en la consola, y el lcov deja archivos en coverage/<nombre-del-proyecto>/. Esta es la tabla del proyecto de referencia, con las filas que importan para este tutorial:

File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 86.81 | 85.89 | 88.46 | 88.11 |
app | 90.24 | 82.35 | 72.72 | 86.2 |
...st.service.ts | 80 | 57.14 | 70 | 75 | 43-45,65
app/album-list | 60.71 | 88.88 | 100 | 55.55 |
album-list.html | 42.1 | 100 | 100 | 38.46 | 6-20

Tres cosas para leerla:

  • Los nombres largos se recortan con ...: ...st.service.ts es artist.service.ts.
  • Una fila por archivo, y una por carpeta que resume los archivos de adentro. También aparecen las plantillas .html, porque el código de la plantilla también se ejecuta.
  • 43-45,65 son líneas de artist.service.ts que ninguna prueba ejecutó: las del método albumResource y la del método crear. Es el primer código sin cubrir.

Tu tabla tendrá otros números y otros archivos. Busca en la columna de líneas sin cubrir un archivo tuyo con pocas pruebas: ese es el que vas a trabajar.


La tabla dice los números de línea, pero no el código. El reporte lcov genera además una página que lo muestra. Ábrela en el navegador:

coverage/<nombre-del-proyecto>/lcov-report/index.html

Entra a un archivo desde la lista. Las líneas que ninguna prueba ejecutó quedan marcadas en rojo, y las ramas que no se tomaron, en amarillo. Abre artist.service.ts y verás marcadas las tres líneas del método albumResource y la del método crear.


6. Cubrir el código que falta: la prueba nueva

Sección titulada «6. Cubrir el código que falta: la prueba nueva»

La línea 65 de artist.service.ts es el POST que guarda un artista nuevo, el método crear del Release 3: ninguna prueba lo ejecuta. Si tu servicio tiene otro método sin cubrir, aplica el mismo patrón con ese. Agrega esta prueba en artist.service.spec.ts, dentro del describe, y el import del modelo arriba:

artist.service.spec.ts
import { NuevoArtista } from './album-list/album';
artist.service.spec.ts
it('crear hace un POST a /artistas con el artista nuevo en el cuerpo', () => {
const nuevo: NuevoArtista = {
nombre: 'Ana',
fotoArtistaUrl: 'https://ejemplo.com/ana.jpg',
paisOrigen: 'Colombia',
biografia: 'Cantautora',
fechaNacimiento: '1990-01-01',
};
service.crear(nuevo).subscribe();
const req = http.expectOne(environment.ARTISTAS_URL);
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual(nuevo);
req.flush({ ...nuevo, id: 1, albumes: [] });
});

Corre la cobertura otra vez. La fila del servicio cambia, y la línea 65 desaparece de la lista:

| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
...st.service.ts | 85 | 57.14 | 80 | 81.25 | 43-45

Antes eran 80, 57.14, 70 y 75 en las cuatro columnas. Subieron tres, y la de ramas no cambió porque crear no tiene ningún if ni ?:. Las líneas 43 a 45, las de albumResource, quedan como ejercicio: una prueba que le pase un id y compruebe la URL de la petición.


7. Una prueba que sube el número sin proteger nada

Sección titulada «7. Una prueba que sube el número sin proteger nada»

Mira esta versión del mismo caso:

artist.service.spec.ts
it('crear se ejecuta', () => {
// `nuevo` es el mismo artista de la prueba anterior.
service.crear(nuevo).subscribe();
http.match(() => true).forEach((req) => req.flush({}));
});

Llama al método y responde cualquier petición, pero no comprueba nada: ni la URL, ni el verbo, ni el cuerpo. Con esa prueba en lugar de la buena, la cobertura es la misma (85, 57.14, 80 y 81.25, y la línea 65 sigue cubierta). Ahora rompe el código: en crear, cambia environment.ARTISTAS_URL por environment.ALBUMES_URL.

Prueba Cobertura del servicio Con la URL rota
crear hace un POST a /artistas con el artista nuevo en el cuerpo 85 / 57.14 / 80 / 81.25 Falla: Expected one matching request for criteria "Match URL: http://localhost:8080/artistas", found none. Requests received are: POST http://localhost:8080/albumes
crear se ejecuta 85 / 57.14 / 80 / 81.25 Pasa: las 16 pruebas en verde

La cobertura no distingue las dos pruebas. Por eso sirve para encontrar código sin pruebas, y no para saber si las pruebas son buenas: eso lo muestra la sección “Romper el código a propósito” del tutorial de Vitest, rompiendo el código a propósito. Restaura la URL y deja la prueba buena.


El Jenkinsfile del curso hace tres cosas con la cobertura del front, sin que tengas que tocar nada:

  1. Instala el medidor si falta. Si el proyecto no trae @vitest/coverage-v8, lo agrega con la versión de vitest.
  2. Corre pnpm test --coverage --coverage-reporters=lcov, que deja coverage/<proyecto>/lcov.info.
  3. Se lo entrega a SonarQube, que lee ese lcov.info y muestra la cobertura del proyecto junto con la del back. Excluye del cálculo main.ts, app.config.ts y la carpeta environments, que no tienen lógica propia.

Por eso no necesitas configurar el pipeline: con las pruebas en tu proyecto, el reporte aparece solo. Lo que sí controlas es qué tanto del código ejecutan esas pruebas, y eso es lo que este tutorial te enseñó a mirar.


  • Code coverage requires either "@vitest/coverage-v8" or .... Falta instalar el medidor (sección 3).
  • Running mixed versions is not supported. El medidor y vitest tienen versiones distintas. Reinstala el medidor con la versión que muestra pnpm list vitest.
  • No encuentras lcov-report ni lcov.info. Corriste con cobertura pero sin --coverage-reporters=lcov, o miras en otra carpeta: está en coverage/<nombre-del-proyecto>/.
  • La cobertura subió pero rompes el código y nada falla. La prueba ejecuta el código sin comprobar nada. Vuelve a la sección 7.
  • pnpm test falla con ERR_PNPM_IGNORED_BUILDS. Es un aviso de pnpm 11 sobre los scripts de instalación, no de tus pruebas: pnpm approve-builds --all lo resuelve.

Al final de este tutorial sabes:

  • Correr las pruebas con cobertura y leer la tabla y el reporte del navegador.
  • Encontrar el código que ninguna prueba ejecuta y escribir la que falta.
  • Distinguir una prueba que protege de una que solo sube el número.
  • Qué hace el pipeline con el reporte.