¿Qué es Lighthouse?
Lighthouse es una herramienta automatizada de código abierto de Google para mejorar la calidad de páginas web. Proporciona auditorías integrales de rendimiento, accesibilidad, progressive web apps, SEO y mejores prácticas. Para ingenieros QA, Lighthouse es esencial para validar que las aplicaciones cumplan estándares modernos de rendimiento web, especialmente Core Web Vitals — las métricas clave de Google para experiencia de usuario.
Por qué Lighthouse Importa para QA
- Core Web Vitals Impactan SEO - Google usa LCP, FID y CLS como factores de ranking
- Validación de Experiencia de Usuario - Cuantifica el rendimiento percibido con métricas reales
- Integración CI/CD - Automatiza pruebas de regresión de rendimiento en pipelines
- Insights Accionables - Obtén recomendaciones específicas para mejoras de rendimiento
- Rendimiento Móvil - Prueba diseño responsive y problemas específicos de móvil
Core Web Vitals Explicados
Core Web Vitals son tres métricas clave que miden la experiencia de usuario en el mundo real:
1. Largest Contentful Paint (LCP)
Qué mide: Rendimiento de carga - cuando el elemento de contenido más grande se vuelve visible.
Umbrales:
- Bueno: ≤ 2.5 segundos
- Necesita Mejora: 2.5 - 4.0 segundos
- Pobre: > 4.0 segundos
Problemas Comunes:
// Problema: JavaScript bloqueante de renderizado
<script src="analytics.js"></script>
// Solución: Diferir scripts no críticos
<script src="analytics.js" defer></script>
// Problema: Imágenes no optimizadas
<img src="hero-10mb.jpg" alt="Hero">
// Solución: Formatos modernos + imágenes responsive
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero" loading="lazy">
</picture>
Estrategias de Optimización:
- Usar CDN para assets estáticos
- Implementar lazy loading para imágenes below-fold
- Precargar recursos críticos:
<link rel="preload" href="hero.jpg" as="image">
- Optimizar tiempos de respuesta del servidor (TTFB < 600ms)
- Usar resource hints:
dns-prefetch
,preconnect
2. First Input Delay (FID) / Interaction to Next Paint (INP)
Qué mide: Interactividad - retraso entre interacción del usuario y respuesta del navegador.
Umbrales FID (siendo reemplazado por INP):
- Bueno: ≤ 100ms
- Necesita Mejora: 100 - 300ms
- Pobre: > 300ms
Umbrales INP (nueva métrica desde 2024):
- Bueno: ≤ 200ms
- Necesita Mejora: 200 - 500ms
- Pobre: > 500ms
Problemas Comunes:
// Problema: Tareas largas de JavaScript bloquean el hilo principal
function processHugeDataset() {
// Loop síncrono procesando 100k items
for (let i = 0; i < 100000; i++) {
heavyCalculation(data[i]);
}
}
// Solución: Dividir en chunks pequeños con setTimeout
async function processHugeDataset() {
const chunkSize = 1000;
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
chunk.forEach(item => heavyCalculation(item));
// Ceder al navegador cada chunk
await new Promise(resolve => setTimeout(resolve, 0));
}
}
// Solución: Usar Web Workers para cálculos pesados
// main.js
const worker = new Worker('calculator.worker.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => {
displayResults(e.data);
};
// calculator.worker.js
self.onmessage = (e) => {
const results = heavyCalculation(e.data);
self.postMessage(results);
};
Estrategias de Optimización:
- Dividir tareas largas en chunks pequeños (< 50ms cada uno)
- Usar code splitting para reducir tamaño de bundle JavaScript
- Diferir scripts de terceros
- Implementar service workers para mejor caching
- Usar
requestIdleCallback
para trabajo no urgente
3. Cumulative Layout Shift (CLS)
Qué mide: Estabilidad visual - cambios inesperados de layout durante carga de página.
Umbrales:
- Bueno: ≤ 0.1
- Necesita Mejora: 0.1 - 0.25
- Pobre: > 0.25
Problemas Comunes:
<!-- Problema: Imágenes sin dimensiones -->
<img src="product.jpg" alt="Product">
<!-- Solución: Siempre especificar width/height -->
<img src="product.jpg" alt="Product" width="800" height="600">
<!-- Problema: Inserción dinámica de contenido -->
<div id="banner"></div>
<script>
// Carga ad asíncronamente, empujando contenido hacia abajo
loadAdvertisement('#banner');
</script>
<!-- Solución: Reservar espacio para contenido dinámico -->
<div id="banner" style="min-height: 250px;">
<!-- Ad se carga aquí sin layout shift -->
</div>
/* Problema: Web fonts causan layout shift (FOIT/FOUT) */
@font-face {
font-family: 'CustomFont';
src: url('custom-font.woff2') format('woff2');
}
/* Solución: Usar font-display para controlar comportamiento de carga */
@font-face {
font-family: 'CustomFont';
src: url('custom-font.woff2') format('woff2');
font-display: swap; /* Mostrar fallback inmediatamente, intercambiar cuando cargue */
}
/* Mejor: Precargar fuentes críticas */
/* En HTML: <link rel="preload" href="custom-font.woff2" as="font" crossorigin> */
Estrategias de Optimización:
- Siempre incluir atributos de tamaño en imágenes y videos
- Reservar espacio para ads y embeds con
min-height
- Evitar insertar contenido arriba de contenido existente
- Usar animaciones
transform
en lugar de propiedades que disparan layout - Precargar fuentes personalizadas y usar
font-display: swap
Ejecutar Pruebas Lighthouse
1. Chrome DevTools (Pruebas Manuales)
# Abrir Chrome DevTools
# 1. Presionar F12 o Cmd+Option+I
# 2. Ir a pestaña "Lighthouse"
# 3. Seleccionar categorías (Performance, Accessibility, etc.)
# 4. Elegir dispositivo (Mobile/Desktop)
# 5. Hacer clic en "Analyze page load"
Mejores Prácticas para Pruebas Manuales:
- Probar en modo Incógnito (sin interferencia de extensiones)
- Usar throttling para simular condiciones del mundo real (Slow 4G, 4x CPU slowdown)
- Ejecutar múltiples pruebas y promediar puntuaciones (resultados pueden variar ±5 puntos)
- Probar configuraciones móvil y desktop
2. Lighthouse CLI (Pruebas Automatizadas)
# Instalar Lighthouse
npm install -g lighthouse
# Prueba básica
lighthouse https://example.com
# Prueba móvil con formatos de salida
lighthouse https://example.com \
--output html,json,csv \
--output-path ./reports/report \
--view \
--chrome-flags="--headless"
# Prueba con throttling específico
lighthouse https://example.com \
--throttling.rttMs=150 \
--throttling.throughputKbps=1600 \
--throttling.cpuSlowdownMultiplier=4
# Probar solo categorías específicas
lighthouse https://example.com \
--only-categories=performance,accessibility
# Archivo de config personalizado
lighthouse https://example.com \
--config-path=./lighthouse-config.js
3. Lighthouse CI (Integración Continua)
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on:
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Start server
run: npm start &
- name: Wait for server
run: npx wait-on http://localhost:3000
- name: Run Lighthouse CI
run: |
npm install -g @lhci/cli
lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: lighthouse-reports
path: .lighthouseci/
Configuración lighthouserc.json:
{
"ci": {
"collect": {
"url": ["http://localhost:3000"],
"numberOfRuns": 3,
"settings": {
"preset": "desktop",
"throttling": {
"rttMs": 40,
"throughputKbps": 10240,
"cpuSlowdownMultiplier": 1
}
}
},
"assert": {
"preset": "lighthouse:recommended",
"assertions": {
"categories:performance": ["error", {"minScore": 0.9}],
"categories:accessibility": ["error", {"minScore": 0.9}],
"first-contentful-paint": ["error", {"maxNumericValue": 2000}],
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"total-blocking-time": ["error", {"maxNumericValue": 300}]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}
Interpretar Puntuaciones Lighthouse
Desglose de Puntuación de Rendimiento
La puntuación de Performance (0-100) se pondera entre múltiples métricas:
Métrica | Peso | Descripción |
---|---|---|
First Contentful Paint (FCP) | 10% | Primer texto/imagen renderizado |
Largest Contentful Paint (LCP) | 25% | Elemento de contenido más grande visible |
Total Blocking Time (TBT) | 30% | Tiempo total que el hilo principal está bloqueado |
Cumulative Layout Shift (CLS) | 25% | Puntuación de estabilidad visual |
Speed Index | 10% | Qué tan rápido se muestra visualmente el contenido |
Rangos de Puntuación:
- 90-100 (Verde): Bueno - cumple mejores prácticas de rendimiento
- 50-89 (Naranja): Necesita mejora - algo de optimización requerida
- 0-49 (Rojo): Pobre - problemas significativos de rendimiento
Estrategia de Presupuesto de Rendimiento
// lighthouserc-budget.json
{
"ci": {
"assert": {
"assertions": {
// Umbrales Core Web Vitals
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"total-blocking-time": ["error", {"maxNumericValue": 300}],
// Presupuestos de recursos
"resource-summary:script:size": ["error", {"maxNumericValue": 300000}],
"resource-summary:image:size": ["error", {"maxNumericValue": 500000}],
"resource-summary:stylesheet:size": ["error", {"maxNumericValue": 50000}],
"resource-summary:document:size": ["error", {"maxNumericValue": 50000}],
"resource-summary:font:size": ["error", {"maxNumericValue": 100000}],
"resource-summary:third-party:size": ["error", {"maxNumericValue": 200000}],
// Presupuestos de cantidad de solicitudes
"resource-summary:script:count": ["error", {"maxNumericValue": 15}],
"resource-summary:third-party:count": ["error", {"maxNumericValue": 10}]
}
}
}
}
Técnicas Avanzadas de Lighthouse
1. Auditorías Personalizadas
// custom-audit.js
class CustomPerformanceAudit {
static get meta() {
return {
id: 'custom-api-performance',
title: 'Tiempo de Respuesta API',
failureTitle: 'Respuestas API son lentas',
description: 'Mide tiempos de respuesta de endpoints API',
requiredArtifacts: ['devtoolsLogs']
};
}
static async audit(artifacts) {
const devtools = artifacts.devtoolsLogs.defaultPass;
const networkRecords = /* extraer registros de red */;
const apiCalls = networkRecords.filter(r =>
r.url.includes('/api/') && r.finished
);
const avgResponseTime = apiCalls.reduce((sum, call) =>
sum + call.endTime - call.startTime, 0
) / apiCalls.length;
const passed = avgResponseTime < 500; // umbral 500ms
return {
score: passed ? 1 : 0,
numericValue: avgResponseTime,
displayValue: `${Math.round(avgResponseTime)}ms promedio`,
details: {
type: 'table',
headings: [
{key: 'url', text: 'URL'},
{key: 'responseTime', text: 'Tiempo Respuesta'}
],
items: apiCalls.map(call => ({
url: call.url,
responseTime: `${Math.round(call.endTime - call.startTime)}ms`
}))
}
};
}
}
module.exports = CustomPerformanceAudit;
2. Pruebas de Flujo de Usuario (Nuevo en Lighthouse 9+)
// user-flow-test.js
import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js';
import puppeteer from 'puppeteer';
async function runUserFlow() {
const browser = await puppeteer.launch({headless: 'new'});
const page = await browser.newPage();
const flow = await startFlow(page, {name: 'Flujo checkout e-commerce'});
// Paso 1: Navegar a homepage
await flow.navigate('https://example.com', {
stepName: 'Homepage'
});
// Paso 2: Buscar producto
await flow.startTimespan({stepName: 'Interacción búsqueda'});
await page.type('#search', 'laptop');
await page.click('#search-button');
await page.waitForSelector('.search-results');
await flow.endTimespan();
// Paso 3: Página detalles producto
await flow.navigate('https://example.com/product/123', {
stepName: 'Página producto'
});
// Paso 4: Interacción añadir al carrito
await flow.startTimespan({stepName: 'Añadir al carrito'});
await page.click('#add-to-cart');
await page.waitForSelector('.cart-notification');
await flow.endTimespan();
// Paso 5: Snapshot checkout
await flow.snapshot({stepName: 'Estado página carrito'});
// Generar reporte
const report = await flow.generateReport();
fs.writeFileSync('user-flow-report.html', report);
await browser.close();
}
runUserFlow();
3. Dashboard de Monitoreo de Rendimiento
# lighthouse-monitor.py
import subprocess
import json
import datetime
from influxdb import InfluxDBClient
def run_lighthouse(url):
"""Ejecutar Lighthouse y retornar métricas"""
result = subprocess.run([
'lighthouse',
url,
'--output=json',
'--output-path=stdout',
'--chrome-flags="--headless"'
], capture_output=True, text=True)
return json.loads(result.stdout)
def extract_metrics(report):
"""Extraer métricas clave del reporte Lighthouse"""
audits = report['audits']
return {
'performance_score': report['categories']['performance']['score'] * 100,
'fcp': audits['first-contentful-paint']['numericValue'],
'lcp': audits['largest-contentful-paint']['numericValue'],
'tbt': audits['total-blocking-time']['numericValue'],
'cls': audits['cumulative-layout-shift']['numericValue'],
'speed_index': audits['speed-index']['numericValue'],
}
def send_to_influxdb(metrics, url):
"""Enviar métricas a InfluxDB para visualización Grafana"""
client = InfluxDBClient(host='localhost', port=8086, database='lighthouse')
point = {
'measurement': 'performance',
'tags': {
'url': url,
'device': 'mobile'
},
'time': datetime.datetime.utcnow().isoformat(),
'fields': metrics
}
client.write_points([point])
# Monitorear páginas cada hora
urls = [
'https://example.com',
'https://example.com/products',
'https://example.com/checkout'
]
for url in urls:
report = run_lighthouse(url)
metrics = extract_metrics(report)
send_to_influxdb(metrics, url)
print(f"✓ {url}: Puntuación Rendimiento {metrics['performance_score']}")
Cuellos de Botella Comunes y Correcciones
Problema: Bundles JavaScript Grandes
# Analizar tamaño de bundle
npm install -g webpack-bundle-analyzer
# Añadir a webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
# Build y analizar
npm run build
Soluciones:
- Code splitting:
import('./heavy-component.js').then(module => ...)
- Tree shaking: Asegurar
"sideEffects": false
en package.json - Eliminar dependencias no usadas
- Usar imports dinámicos para code splitting basado en rutas
Problema: Recursos Bloqueantes de Renderizado
<!-- Problema: CSS bloqueante -->
<link rel="stylesheet" href="styles.css">
<!-- Solución: CSS crítico inline, diferir no crítico -->
<style>
/* CSS crítico above-fold inline */
.header { /* ... */ }
.hero { /* ... */ }
</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
Problema: Imágenes No Optimizadas
# Instalar herramientas de optimización de imágenes
npm install -D imagemin imagemin-webp imagemin-avif
# optimize-images.js
const imagemin = require('imagemin');
const imageminWebp = require('imagemin-webp');
const imageminAvif = require('imagemin-avif');
(async () => {
await imagemin(['images/*.{jpg,png}'], {
destination: 'images/optimized',
plugins: [
imageminWebp({quality: 80}),
imageminAvif({quality: 65})
]
});
})();
Conclusión
Lighthouse es una herramienta indispensable para pruebas modernas de rendimiento web. Al integrar Lighthouse en tu flujo de trabajo QA — desde auditorías manuales DevTools hasta pipelines CI/CD (como se discute en Load Testing with JMeter: Complete Guide) automatizados — aseguras que las aplicaciones cumplan estándares Core Web Vitals y entreguen experiencias excepcionales de usuario.
Conclusiones Clave:
- Enfócate en Core Web Vitals: LCP, INP/FID, CLS
- Automatiza pruebas Lighthouse en pipelines CI/CD
- (como se discute en Burp Suite for QA Engineers: Complete Security Testing Guide) Establece presupuestos de rendimiento y aplícalos
- Usa pruebas User Flow para interacciones complejas
- Monitorea rendimiento en el tiempo con dashboards
- Optimiza imágenes, JavaScript y rutas de renderizado
El rendimiento es una característica, no una reflexión tardía. Haz de las pruebas Lighthouse una parte central de tu proceso QA.