🛠️ Herramientas y Scripts
Objetivo: Configurar las herramientas esenciales para ejecutar tests con cobertura de código de forma local, usando ReportGenerator para generar reportes visuales.
📦 Instalación de ReportGenerator
Section titled “📦 Instalación de ReportGenerator”¿Qué es ReportGenerator?
Section titled “¿Qué es ReportGenerator?”ReportGenerator es la herramienta estándar para convertir reportes de cobertura XML en reportes HTML visuales con métricas detalladas, gráficos y trending histórico.
Instalación Global
Section titled “Instalación Global”# Instalar como herramienta global de .NETdotnet tool install --global dotnet-reportgenerator-globaltool
# Verificar instalaciónreportgenerator -help
# Actualizar si ya está instaladodotnet tool update --global dotnet-reportgenerator-globaltool🧪 Comandos Esenciales
Section titled “🧪 Comandos Esenciales”Comando Base para Tests con Cobertura
Section titled “Comando Base para Tests con Cobertura”dotnet test --filter FullyQualifiedName!~IntegrationTest /p:CollectCoverage=true /p:CoverletOutputFormat="cobertura%2cjson" /p:CoverletOutput=../coverage-reports/ /p:MergeWith="../coverage-reports/coverage.json" -m:1Desglose del comando:
| Parámetro | Propósito | Detalle |
|---|---|---|
--filter FullyQualifiedName!~IntegrationTest | Filtrar tests | Excluye tests de integración |
/p:CollectCoverage=true | Habilitar cobertura | Activa coverlet para análisis |
/p:CoverletOutputFormat="cobertura%2cjson" | Formatos de salida | Genera XML (para ReportGenerator) y JSON (para merge) |
/p:CoverletOutput=../coverage-reports/ | Carpeta estándar | Todos usamos la misma carpeta |
/p:MergeWith="../coverage-reports/coverage.json" | Merge de resultados | Combina múltiples ejecuciones |
-m:1 | Ejecución secuencial | Evita conflicts en archivos de cobertura |
Comando para Generar Reporte HTML
Section titled “Comando para Generar Reporte HTML”reportgenerator "-reports:coverage-reports\coverage.cobertura.xml;coverage-reports\coverage.net48.cobertura.xml;coverage-reports\coverage.net8.0.cobertura.xml" -targetdir:coverage-reports/html -historydir:coverage-reports/html/history -classfilters:'-Component.Cache.Models.*;'Desglose del comando:
| Parámetro | Propósito | Detalle |
|---|---|---|
-reports:coverage-reports\*.xml | Archivos fuente | Múltiples archivos XML se combinan automáticamente |
-targetdir:coverage-reports/html | Carpeta de salida | Reporte HTML generado aquí |
-historydir:coverage-reports/html/history | Histórico de cobertura | Tracking de tendencias a lo largo del tiempo |
-classfilters:'-Component.Cache.Models.*;' | Filtros de clases | Excluye models, DTOs y clases auto-generadas |
📋 Scripts Listos para Usar
Section titled “📋 Scripts Listos para Usar”Script Principal: test-coverage.ps1
Section titled “Script Principal: test-coverage.ps1”# Script simple para tests con coberturaparam( [string]$ProjectPath = ".", [switch]$OpenReport, [switch]$CleanFirst)
# Colores para outputfunction Write-Info { param($msg) Write-Host $msg -ForegroundColor Cyan }function Write-Success { param($msg) Write-Host $msg -ForegroundColor Green }function Write-Error { param($msg) Write-Host $msg -ForegroundColor Red }
Write-Info "🧪 Ejecutando tests con cobertura..."
# Limpiar reportes anteriores si se solicitaif ($CleanFirst -and (Test-Path "coverage-reports")) { Remove-Item -Path "coverage-reports" -Recurse -Force Write-Info "🧹 Reportes anteriores eliminados"}
# Ejecutar tests con coberturaWrite-Info "🚀 Ejecutando tests..."dotnet test $ProjectPath ` --filter "FullyQualifiedName!~IntegrationTest" ` /p:CollectCoverage=true ` /p:CoverletOutputFormat="cobertura%2cjson" ` /p:CoverletOutput=../coverage-reports/ ` /p:MergeWith="../coverage-reports/coverage.json" ` -m:1
if ($LASTEXITCODE -ne 0) { Write-Error "❌ Tests fallaron" exit 1}
# Verificar que se generó el archivo de coberturaif (-not (Test-Path "coverage-reports\coverage.cobertura.xml")) { Write-Error "❌ No se generó archivo de cobertura" exit 1}
# Generar reporte HTML con ReportGeneratorWrite-Info "📊 Generando reporte HTML..."reportgenerator ` "-reports:coverage-reports\coverage.cobertura.xml" ` "-targetdir:coverage-reports/html" ` "-historydir:coverage-reports/html/history" ` "-classfilters:-*.Tests.*;-*.Models.*;"
if ($LASTEXITCODE -eq 0) { Write-Success "✅ Reporte generado exitosamente" Write-Info "📁 Ubicación: coverage-reports/html/index.html"
if ($OpenReport) { Start-Process "coverage-reports/html/index.html" Write-Info "🌐 Abriendo reporte en navegador..." }} else { Write-Error "❌ Error generando reporte HTML" exit 1}
Write-Success "🎉 Proceso completado"# Script rápido para development - sin coberturaparam( [string]$Filter = "", [string]$Framework = "")
function Write-Info { param($msg) Write-Host $msg -ForegroundColor Cyan }function Write-Success { param($msg) Write-Host $msg -ForegroundColor Green }
Write-Info "⚡ Ejecutando tests rápidos..."
$args = @("test", "--verbosity", "minimal")
if ($Filter) { $args += "--filter", $Filter Write-Info "🔍 Filtro: $Filter"}
if ($Framework) { $args += "--framework", $Framework Write-Info "🎯 Framework: $Framework"}
$sw = [Diagnostics.Stopwatch]::StartNew()& dotnet @args$sw.Stop()
if ($LASTEXITCODE -eq 0) { Write-Success "✅ Tests completados en $($sw.Elapsed.TotalSeconds.ToString('F2')) segundos"} else { Write-Host "❌ Algunos tests fallaron" -ForegroundColor Red exit 1}Uso de los Scripts
Section titled “Uso de los Scripts”# Ejecución básica con cobertura.\test-coverage.ps1
# Con reporte automático.\test-coverage.ps1 -OpenReport
# Limpiar reportes anteriores.\test-coverage.ps1 -CleanFirst -OpenReport
# Tests rápidos para desarrollo.\test-quick.ps1
# Tests específicos.\test-quick.ps1 -Filter "CacheServiceTests"
# Framework específico.\test-quick.ps1 -Framework net8.0🗂️ Estructura de Archivos
Section titled “🗂️ Estructura de Archivos”Carpeta Estándar de Cobertura
Section titled “Carpeta Estándar de Cobertura”📁 tu-proyecto/├── 📁 coverage-reports/ # ← Carpeta estándar del equipo│ ├── 📄 coverage.cobertura.xml # Archivo XML principal│ ├── 📄 coverage.json # Para merge de múltiples runs│ ├── 📄 coverage.net48.cobertura.xml # Multi-targeting│ ├── 📄 coverage.net8.0.cobertura.xml # Multi-targeting│ └── 📁 html/ # Reporte HTML│ ├── 📄 index.html # ← Abrir este archivo│ ├── 📁 history/ # Trending histórico│ └── 📁 assets/ # CSS, JS, imágenes├── 📄 test-coverage.ps1 # Script principal└── 📄 test-quick.ps1 # Script para desarrolloArchivos en .gitignore
Section titled “Archivos en .gitignore”# Agregar a .gitignorecoverage-reports/*.cobertura.xmlcoverage.json🎯 Filtros Comunes
Section titled “🎯 Filtros Comunes”Filtros de Tests
Section titled “Filtros de Tests”| Filtro | Propósito | Comando |
|---|---|---|
| Excluir integración | FullyQualifiedName!~IntegrationTest | Por defecto |
| Solo un test class | ClassName=CacheServiceTests | Tests específicos |
| Solo un método | Name~GetAsync | Método específico |
| Múltiples clases | ClassName=CacheServiceTests|UserServiceTests | Varias clases |
Filtros de Clases (ReportGenerator)
Section titled “Filtros de Clases (ReportGenerator)”# Filtros comunes para excluir del reporte-classfilters:'-*.Tests.*;-*.Models.*;-*.DTOs.*;-*Migrations*;-Program;-Startup'
# Filtros específicos del equipo-classfilters:'-Component.Cache.Models.*;-*.TestData.*;-*Generated*'
# Solo incluir clases específicas-classfilters:'+MyProject.Services.*;+MyProject.Repositories.*'📊 Interpretación del Reporte
Section titled “📊 Interpretación del Reporte”Métricas Principales
Section titled “Métricas Principales”| Métrica | Descripción | Objetivo |
|---|---|---|
| Line Coverage | % de líneas ejecutadas | ≥ 90% |
| Branch Coverage | % de ramas condicionales ejecutadas | ≥ 85% |
| Method Coverage | % de métodos ejecutados | ≥ 85% |
Navegación del Reporte HTML
Section titled “Navegación del Reporte HTML”- 📊 Dashboard Principal: Métricas generales y trending
- 📁 Assembly View: Cobertura por ensamblado
- 📄 Class View: Cobertura por clase individual
- 🔍 Line-by-Line: Líneas específicas cubiertas/no cubiertas
- 📈 History: Tendencia histórica de cobertura
Códigos de Color
Section titled “Códigos de Color”- 🟢 Verde: Cobertura alta (≥ 80%)
- 🟡 Amarillo: Cobertura media (50-80%)
- 🔴 Rojo: Cobertura baja (< 50%)
- ⚫ Gris: Código excluido o no ejecutable
⚡ Tips para el Día a Día
Section titled “⚡ Tips para el Día a Día”Workflow Recomendado
Section titled “Workflow Recomendado”# 1. Desarrollo activo - tests rápidos.\test-quick.ps1 -Filter "MiClaseTests"
# 2. Antes de commit - cobertura completa.\test-coverage.ps1 -CleanFirst -OpenReport
# 3. Review de PR - verificar trending# (Revisar history en reporte HTML)Comandos de Troubleshooting
Section titled “Comandos de Troubleshooting”# Verificar instalación de ReportGeneratorreportgenerator -version
# Reinstalar si hay problemasdotnet tool uninstall --global dotnet-reportgenerator-globaltooldotnet tool install --global dotnet-reportgenerator-globaltool
# Limpiar cache de .NETdotnet nuget locals all --clear
# Verificar proyectos de testdotnet test --list-testsErrores Comunes
Section titled “Errores Comunes”| Error | Causa | Solución |
|---|---|---|
'reportgenerator' is not recognized | No instalado globalmente | dotnet tool install --global dotnet-reportgenerator-globaltool |
No coverage files found | Tests no ejecutados con cobertura | Verificar parámetros /p:CollectCoverage=true |
Access denied to coverage-reports | Archivos bloqueados | Cerrar Visual Studio y ejecutar .\test-coverage.ps1 -CleanFirst |
Multiple frameworks conflict | Merge de archivos problemático | Usar nombres específicos con -CoverletOutputName |
✅ Checklist de Setup
Section titled “✅ Checklist de Setup”Instalación Inicial
Section titled “Instalación Inicial”- ReportGenerator instalado globalmente
- Scripts descargados en carpeta del proyecto
- coverage-reports/ agregado a .gitignore
- Permisos de PowerShell configurados
Validación
Section titled “Validación”-
.\test-coverage.ps1ejecuta sin errores - Reporte HTML se genera correctamente
- Métricas muestran valores realistas
- History funciona en ejecuciones posteriores
Integración del Equipo
Section titled “Integración del Equipo”- Todos los devs tienen scripts funcionando
- Carpeta estándar
coverage-reports/usada por todos - Filtros acordados para clases excluidas
- Umbrales de cobertura definidos (90% líneas, 85% branches)