Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Descripción General
La cobertura de código mide qué líneas, branches y condiciones fueron ejecutadas durante los tests. Es un proxy útil para código no testeado, pero no una medida de calidad de test — 100% de cobertura sin assertions es meaningless. El patron a continuacion demuestra cómo recolectar, reportar y configurar thresholds de cobertura significativos sin crear incentivos perversos.
Cuándo Usar
-
For alternatives, see Measure Test Coverage with pytest-cov.
-
Necesitas visibilidad sobre qué rutas de código carecen de ejecución de test
-
Los pipelines de CI necesitan una puerta para prevenir código no testeado de ser mergeado
-
Estás refactorizando código legacy y quieres asegurar que los cambios nuevos están testeados
-
Los equipos necesitan una métrica compartida para rastrear progreso de testing con el tiempo
-
Quieres identificar código muerto que nunca se ejecuta en producción o tests
Cuándo NO Usar
- La cobertura se trata como un objetivo (ej. “debe ser 90%”) en lugar de una guía — esto lleva a tests sin assertions
- El codebase es un prototipo o spike que será descartado — la cobertura no añade valor
- Estás testeando código generado, boilerplate de framework o archivos de configuración
- El equipo optimiza porcentaje de cobertura sobre encontrar bugs reales
Implementación Paso a Paso
Python (pytest-cov)
# Instalar
pip install pytest-cov
# Ejecutar con reporte de terminal
pytest --cov=myproject --cov-report=term-missing tests/
# Generar reporte HTML
pytest --cov=myproject --cov-report=html --cov-report=xml tests/
# Fallar bajo threshold (hecho cumplir en CI)
pytest --cov=myproject --cov-fail-under=80 tests/
# Branch coverage (rastrea si if/else ambos tomados)
pytest --cov=myproject --cov-branch tests/
# Configuración pyproject.toml
[tool.coverage.run]
source = ["myproject"]
branch = true
omit = [
"*/tests/*",
"*/migrations/*",
"*/venv/*",
]
[tool.coverage.report]
precision = 2
fail_under = 80
skip_covered = true
show_missing = true
[tool.coverage.html]
directory = "htmlcov"
[tool.coverage.xml]
output = "coverage.xml"
# Ejecutando en CI con múltiples markers
pytest -m "not slow" --cov=myproject --cov-report=xml --cov-fail-under=80
JavaScript (nyc / c8)
# c8 es la moderna herramienta de cobertura nativa V8 rápida
npm install --save-dev c8
# Ejecutar tests con cobertura
npx c8 npm test
# Reporte HTML
npx c8 --reporter=html --reporter=text npm test
# Fallar bajo threshold
npx c8 --check-coverage --lines 80 --functions 80 --branches 75 npm test
# Excluir archivos de cobertura
npx c8 --exclude="src/**/*.test.js" --exclude="src/vendor/**" npm test
// package.json
{
"scripts": {
"test": "vitest run",
"test:coverage": "vitest run --coverage"
},
"devDependencies": {
"@vitest/coverage-v8": "^1.0.0",
"vitest": "^1.0.0"
}
}
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'json'],
lines: 80,
functions: 80,
branches: 75,
statements: 80,
exclude: [
'**/*.test.ts',
'**/tests/**',
'**/node_modules/**',
'**/vendor/**'
]
}
}
});
Java (JaCoCo)
<!-- pom.xml -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.75</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
# Generar reporte
mvn jacoco:report
# Verificar thresholds
mvn jacoco:check
# Generar badge para README
mvn jacoco:report && cat target/site/jacoco/index.html | grep -oP 'Total[^%]+%'
Integración CI
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install pytest pytest-cov
- run: pytest --cov=myproject --cov-report=xml --cov-fail-under=80
- uses: codecov/codecov-action@v3
with:
files: ./coverage.xml
Lo que funciona
- Mide branch coverage, no solo line coverage. Una sola línea con
if x:reporta como cubierta si la rama true se ejecuta, incluso si la rama false nunca se testea. Branch coverage detecta esto. - Configura thresholds por módulo, no globalmente. La lógica de negocio core debería tener thresholds más altos (85-90%) que el código glue de UI o archivos auto-generados (50-60%).
- Excluye código de infraestructura de los objetivos. Migraciones de base de datos, clientes gRPC generados y archivos de config no deberían contar contra tu métrica de cobertura.
- Rastrea tendencias de cobertura, no números absolutos. Una caída de 5% en un PR es más útil que “estamos en 82% hoy”.
- Revisa líneas no cubiertas en PRs, no solo el porcentaje. Un bot de comentarios que lista las 3 líneas no cubiertas es más útil que un checkmark rojo al 79%.
Errores Comunes
- Hacer cumplir 100% de cobertura. Incentiva tests que ejecutan código sin asertar comportamiento, o anotaciones
@excludepara gamear la métrica. - Solo medir line coverage. Una función con 10 branches puede mostrar 100% de line coverage mientras solo 2 branches están testeados.
- Incluir archivos de test en cobertura. Utilidades de test y clases mock inflan el número y ocultan cobertura de producción faltante.
- Comparar cobertura entre lenguajes. Python branch coverage y Java line coverage no son métricas comparables — rastrea tendencias dentro de cada codebase.
- Ignorar cobertura en tests de integración. Los tests de integración lentos a menudo cubiertan las rutas más importantes; excluirlos de cobertura oculta gaps reales.
Lectura Adicional
- Documentación oficial: consulta la referencia actualizada del framework o herramienta utilizada.
- Guías relacionadas: explora las guías de testing y coverage para profundizar.
- Patrones complementarios: revisa los patrones de diseño aplicables a tu stack tecnológico.
- Postmortems públicos: estudia incidentes reales de equipos que enfrentaron problemas similares en producción.
Notas de Producción
- Despliega gradualmente usando canary o blue-green para detectar regresiones temprano.
- Configura alertas para errores, latencia p99 y tasa de fallos antes de habilitar en producción.
- Documenta el rollback en el runbook; prueba el procedimiento en staging al menos una vez por trimestre.
- Revisa logs estructurados con correlation IDs para trazar requests end-to-end en incidentes.
Puntos Clave
- Aplica medir cobertura de test cuando necesites una solución práctica para tu caso de uso.
- Monitorea el rendimiento después de implementar; mide latencia, errores y uso de recursos antes y después.
- Revisa la sección de Troubleshooting ante errores comunes; la mayoría tienen causa raíz documentada con solución.
- Mantén dependencias actualizadas y ejecuta tests en CI para prevenir regresiones en producción.
Troubleshooting
- Flaky tests: isolate shared state, time, and randomness. Make tests independent and deterministic; quarantine persistently flaky tests.
- High coverage but bugs in production: coverage does not guarantee correctness. Add mutation testing, property-based tests, or contract tests.
- Slow test suite: parallelize, mock slow dependencies, and avoid end-to-end tests for logic that can be unit tested.
- Tests pass locally but fail in CI: check environment differences, timezone, locale, and dependency versions. Pin tool versions.
- Debugging a failing integration test: Reset state before each test.
Errores Comunes en Producción
- Copiar el ejemplo sin adaptarlo a volúmenes y modos de fallo reales.
- Saltar tests de carga e inyección de errores antes del primer despliegue productivo.
- Codificar valores fijos que deberían ser configurables por entorno.
- Olvidar agregar logging y monitoreo en cada paso.
- Desplegar sin plan de rollback ni estrategia de backup probada.
- Asumir que el ejemplo mínimo escalará sin agregar caché o procesamiento por lotes.
- No documentar la versión y configuración usadas en producción.
- Dejar la receta sin cambios cuando evolucionan las dependencias o la escala.
Related Resources
Configurar Fixtures de Test
Cómo gestionar fixtures de test con patrones factory, hooks de setup/teardown y datos deterministas para tests unitarios e integración confiables en Python, JavaScript y Java.
RecipeGenerar Datos de Test
Cómo generar datos de test realistas y deterministas con Faker, factory-boy y generadores type-aware para suites de test confiables en Python, JavaScript y Java.
GuideGuía de Estrategia de Testing
Una guía práctica para construir una estrategia de testing en capas con unit, integration y end-to-end tests.
Preguntas frecuentes
- ¿Es 100% de cobertura un buen objetivo?
- 100% de cobertura de líneas es alcanzable pero puede ser engañoso. Un número alto de cobertura con aserciones débiles no significa que el código esté bien probado. Apunta a una cobertura...
- ¿Cuál es la diferencia entre cobertura de líneas y de ramas?
- La cobertura de líneas cuenta líneas ejecutadas. La cobertura de ramas cuenta si cada rama de decisión (if/else, switch) fue tomada. La cobertura de ramas suele revelar más caminos no probados.
- ¿Cómo debo usar cobertura en CI?
- Establece umbrales mínimos para módulos críticos, rastrea tendencias a lo largo del tiempo y rechaza pull requests que bajen considerablemente la cobertura sin justificación. Evita jugar con la...