Skip to content

🛠️ 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.


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.

Terminal window
# Instalar como herramienta global de .NET
dotnet tool install --global dotnet-reportgenerator-globaltool
# Verificar instalación
reportgenerator -help
# Actualizar si ya está instalado
dotnet tool update --global dotnet-reportgenerator-globaltool

Terminal window
dotnet test --filter FullyQualifiedName!~IntegrationTest /p:CollectCoverage=true /p:CoverletOutputFormat="cobertura%2cjson" /p:CoverletOutput=../coverage-reports/ /p:MergeWith="../coverage-reports/coverage.json" -m:1

Desglose del comando:

ParámetroPropósitoDetalle
--filter FullyQualifiedName!~IntegrationTestFiltrar testsExcluye tests de integración
/p:CollectCoverage=trueHabilitar coberturaActiva coverlet para análisis
/p:CoverletOutputFormat="cobertura%2cjson"Formatos de salidaGenera XML (para ReportGenerator) y JSON (para merge)
/p:CoverletOutput=../coverage-reports/Carpeta estándarTodos usamos la misma carpeta
/p:MergeWith="../coverage-reports/coverage.json"Merge de resultadosCombina múltiples ejecuciones
-m:1Ejecución secuencialEvita conflicts en archivos de cobertura
Terminal window
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ámetroPropósitoDetalle
-reports:coverage-reports\*.xmlArchivos fuenteMúltiples archivos XML se combinan automáticamente
-targetdir:coverage-reports/htmlCarpeta de salidaReporte HTML generado aquí
-historydir:coverage-reports/html/historyHistórico de coberturaTracking de tendencias a lo largo del tiempo
-classfilters:'-Component.Cache.Models.*;'Filtros de clasesExcluye models, DTOs y clases auto-generadas

test-coverage.ps1
# Script simple para tests con cobertura
param(
[string]$ProjectPath = ".",
[switch]$OpenReport,
[switch]$CleanFirst
)
# Colores para output
function 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 solicita
if ($CleanFirst -and (Test-Path "coverage-reports")) {
Remove-Item -Path "coverage-reports" -Recurse -Force
Write-Info "🧹 Reportes anteriores eliminados"
}
# Ejecutar tests con cobertura
Write-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 cobertura
if (-not (Test-Path "coverage-reports\coverage.cobertura.xml")) {
Write-Error "❌ No se generó archivo de cobertura"
exit 1
}
# Generar reporte HTML con ReportGenerator
Write-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"
Terminal window
# 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

📁 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 desarrollo
# Agregar a .gitignore
coverage-reports/
*.cobertura.xml
coverage.json

FiltroPropósitoComando
Excluir integraciónFullyQualifiedName!~IntegrationTestPor defecto
Solo un test classClassName=CacheServiceTestsTests específicos
Solo un métodoName~GetAsyncMétodo específico
Múltiples clasesClassName=CacheServiceTests|UserServiceTestsVarias clases
Terminal window
# 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.*'

MétricaDescripciónObjetivo
Line Coverage% de líneas ejecutadas≥ 90%
Branch Coverage% de ramas condicionales ejecutadas≥ 85%
Method Coverage% de métodos ejecutados≥ 85%
  1. 📊 Dashboard Principal: Métricas generales y trending
  2. 📁 Assembly View: Cobertura por ensamblado
  3. 📄 Class View: Cobertura por clase individual
  4. 🔍 Line-by-Line: Líneas específicas cubiertas/no cubiertas
  5. 📈 History: Tendencia histórica de cobertura
  • 🟢 Verde: Cobertura alta (≥ 80%)
  • 🟡 Amarillo: Cobertura media (50-80%)
  • 🔴 Rojo: Cobertura baja (< 50%)
  • Gris: Código excluido o no ejecutable

Terminal window
# 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)
Terminal window
# Verificar instalación de ReportGenerator
reportgenerator -version
# Reinstalar si hay problemas
dotnet tool uninstall --global dotnet-reportgenerator-globaltool
dotnet tool install --global dotnet-reportgenerator-globaltool
# Limpiar cache de .NET
dotnet nuget locals all --clear
# Verificar proyectos de test
dotnet test --list-tests
ErrorCausaSolución
'reportgenerator' is not recognizedNo instalado globalmentedotnet tool install --global dotnet-reportgenerator-globaltool
No coverage files foundTests no ejecutados con coberturaVerificar parámetros /p:CollectCoverage=true
Access denied to coverage-reportsArchivos bloqueadosCerrar Visual Studio y ejecutar .\test-coverage.ps1 -CleanFirst
Multiple frameworks conflictMerge de archivos problemáticoUsar nombres específicos con -CoverletOutputName

  • ReportGenerator instalado globalmente
  • Scripts descargados en carpeta del proyecto
  • coverage-reports/ agregado a .gitignore
  • Permisos de PowerShell configurados
  • .\test-coverage.ps1 ejecuta sin errores
  • Reporte HTML se genera correctamente
  • Métricas muestran valores realistas
  • History funciona en ejecuciones posteriores
  • 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)