El testing manual de sitios web es lento, propenso a errores y no escala. Te muestro cómo implementar una suite de testing automática que valida todo: desde SEO hasta performance.
El Problema del Testing Manual
La mayoría de desarrolladores validan sus sitios manualmente:
# Proceso manual típico
1. Abrir Lighthouse
2. Revisar meta tags manualmente
3. Validar Schema.org en herramientas online
4. Probar responsive design en DevTools
5. Verificar accesibilidad con axe
6. Repetir para cada página...
Problemas comunes:
- Tiempo: Horas de testing manual
- Inconsistencia: Diferentes criterios cada vez
- Errores: Fácil pasar por alto problemas
- Escalabilidad: No funciona con muchas páginas
- Regresiones: Cambios rompen funcionalidad existente
La Solución: Testing Automatizado Completo
Mi sistema ejecuta 278+ tests automáticos que validan todo el sitio en minutos:
// Una sola línea ejecuta TODOS los tests
npm run test:all
// Ejecuta automáticamente:
// - 30 tests de Meta Tags
// - 36 tests de Dark Mode
// - 27 tests de Image Optimization
// - 33 tests de Reading Time
// - 22 tests de Social Share
// - 35 tests de AI Metadata
// - Tests de SEO en producción
// - Tests de performance
// - Validación de Schema.org
Arquitectura de Testing
1. Unit Tests: Features Modulares
// src/features/meta-tags/__tests__/meta-tags.test.ts
import { describe, it, expect } from 'vitest';
import { MetaTagGenerator } from '../engine/generator.ts';
describe('Meta Tags Feature', () => {
const generator = new MetaTagGenerator();
describe('Basic Meta Tags', () => {
it('should generate title and description', () => {
const tags = generator.generateBasicTags({
title: 'Test Page',
description: 'Test description'
});
expect(tags).toContainEqual({
name: 'title',
content: 'Test Page'
});
expect(tags).toContainEqual({
name: 'description',
content: 'Test description'
});
});
it('should validate title length', () => {
const longTitle = 'A'.repeat(100);
expect(() => {
generator.generateBasicTags({
title: longTitle,
description: 'Valid description'
});
}).toThrow('Title too long');
});
});
describe('Open Graph Tags', () => {
it('should generate complete OG tags', () => {
const tags = generator.generateOpenGraphTags({
title: 'Test Page',
description: 'Test description',
image: '/test-image.jpg',
url: '/test-page'
});
expect(tags).toContainEqual({
property: 'og:title',
content: 'Test Page'
});
expect(tags).toContainEqual({
property: 'og:type',
content: 'website'
});
});
});
});
2. Integration Tests: Features Trabajando Juntas
// src/__tests__/integration/seo-integration.test.ts
import { describe, it, expect } from 'vitest';
import { generateMetaTags } from '../features/meta-tags';
import { generateSchema } from '../features/schema';
describe('SEO Integration Tests', () => {
it('should generate consistent data across features', () => {
const pageData = {
title: 'Test Article',
description: 'Test description',
url: '/blog/test-article',
type: 'article' as const,
publishedDate: new Date('2024-06-02')
};
// Generar meta tags
const metaTags = generateMetaTags(pageData);
// Generar schema
const schema = generateSchema(pageData);
// Validar consistencia
expect(metaTags.title).toBe(schema.headline);
expect(metaTags.description).toBe(schema.description);
expect(metaTags.canonicalUrl).toBe(schema.mainEntityOfPage['@id']);
});
});
3. Production Tests: Validación en Vivo
// src/__tests__/seo/production.test.ts
import { describe, it, expect, beforeAll } from 'vitest';
import { JSDOM } from 'jsdom';
const PRODUCTION_URL = 'https://cappato.dev';
describe('SEO Production Tests', () => {
let dom: JSDOM;
let document: Document;
beforeAll(async () => {
const response = await fetch(PRODUCTION_URL);
const html = await response.text();
dom = new JSDOM(html);
document = dom.window.document;
});
describe('Meta Tags Validation', () => {
it('should have proper title tag', () => {
const title = document.querySelector('title')?.textContent;
expect(title).toBeTruthy();
expect(title!.length).toBeGreaterThan(10);
expect(title!.length).toBeLessThan(60);
});
it('should have meta description', () => {
const description = document.querySelector('meta[name="description"]')?.getAttribute('content');
expect(description).toBeTruthy();
expect(description!.length).toBeGreaterThan(50);
expect(description!.length).toBeLessThan(160);
});
it('should have Open Graph tags', () => {
const ogTitle = document.querySelector('meta[property="og:title"]')?.getAttribute('content');
const ogDescription = document.querySelector('meta[property="og:description"]')?.getAttribute('content');
const ogImage = document.querySelector('meta[property="og:image"]')?.getAttribute('content');
expect(ogTitle).toBeTruthy();
expect(ogDescription).toBeTruthy();
expect(ogImage).toBeTruthy();
expect(ogImage).toMatch(/^https?:\/\//);
});
});
describe('Schema.org Validation', () => {
it('should have valid JSON-LD schemas', () => {
const scripts = document.querySelectorAll('script[type="application/ld+json"]');
expect(scripts.length).toBeGreaterThan(0);
scripts.forEach(script => {
const content = script.textContent;
expect(content).toBeTruthy();
// Validar que es JSON válido
const schema = JSON.parse(content!);
expect(schema['@context']).toBe('https://schema.org');
expect(schema['@type']).toBeTruthy();
});
});
});
});
4. Performance Tests: Core Web Vitals
// src/__tests__/seo/performance.test.ts
import { describe, it, expect } from 'vitest';
describe('Performance Tests', () => {
describe('Response Times', () => {
it('should respond quickly', async () => {
const start = Date.now();
const response = await fetch('https://cappato.dev');
const timing = Date.now() - start;
expect(response.status).toBe(200);
expect(timing).toBeLessThan(1000); // < 1 segundo
});
});
describe('Resource Optimization', () => {
it('should have optimized images', async () => {
const response = await fetch('https://cappato.dev');
const html = await response.text();
const dom = new JSDOM(html);
const images = dom.window.document.querySelectorAll('img');
images.forEach(img => {
// Validar alt text
expect(img.getAttribute('alt')).toBeTruthy();
// Validar lazy loading
const loading = img.getAttribute('loading');
if (loading) {
expect(['lazy', 'eager']).toContain(loading);
}
// Validar formatos modernos
const src = img.getAttribute('src');
if (src) {
expect(src).toMatch(/\.(webp|avif|jpg|jpeg|png)$/i);
}
});
});
it('should have minimal JavaScript', async () => {
const response = await fetch('https://cappato.dev');
const html = await response.text();
const dom = new JSDOM(html);
const scripts = dom.window.document.querySelectorAll('script[src]');
// Sitio estático debería tener JS mínimo
expect(scripts.length).toBeLessThan(5);
});
});
describe('Core Web Vitals Support', () => {
it('should have elements that support good CLS', async () => {
const response = await fetch('https://cappato.dev');
const html = await response.text();
const dom = new JSDOM(html);
const images = dom.window.document.querySelectorAll('img');
let imagesWithDimensions = 0;
images.forEach(img => {
const width = img.getAttribute('width');
const height = img.getAttribute('height');
if (width && height) {
imagesWithDimensions++;
}
});
if (images.length > 0) {
const dimensionRatio = imagesWithDimensions / images.length;
expect(dimensionRatio).toBeGreaterThan(0.25); // Al menos 25%
}
});
});
});
Configuración de Testing
Vitest Config Optimizada
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
// Configuración para diferentes tipos de tests
include: [
'src/**/*.{test,spec}.{js,ts}',
'src/__tests__/**/*.{js,ts}'
],
// Timeouts apropiados
testTimeout: 30000, // 30s para tests de producción
hookTimeout: 10000, // 10s para setup/teardown
// Configuración de entorno
environment: 'jsdom',
globals: true,
// Coverage
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'dist/',
'**/*.d.ts',
'**/*.config.*'
]
},
// Configuración para tests de producción
env: {
PRODUCTION_URL: 'https://cappato.dev'
}
}
});
Scripts NPM Organizados
{
"scripts": {
"test": "vitest",
"test:unit": "vitest src/features",
"test:integration": "vitest src/__tests__/integration",
"test:seo": "vitest src/__tests__/seo",
"test:seo:production": "vitest src/__tests__/seo/production.test.ts",
"test:seo:performance": "vitest src/__tests__/seo/performance.test.ts",
"test:all": "npm run test:unit && npm run test:integration && npm run test:seo",
"test:coverage": "vitest --coverage",
"test:watch": "vitest --watch"
}
}
🤖 Automatización con GitHub Actions
CI/CD Pipeline
# .github/workflows/test.yml
name: Automated Testing
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit
- name: Run integration tests
run: npm run test:integration
- name: Build site
run: npm run build
- name: Run SEO tests (if deployed)
if: github.ref == 'refs/heads/main'
run: npm run test:seo:production
env:
PRODUCTION_URL: ${{ secrets.PRODUCTION_URL }}
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage/coverage-final.json
Resultados y Métricas
Dashboard de Testing
UNIT TESTS: 278/302 tests passing (92% success rate)
├── Meta Tags: 30/30
├── Dark Mode: 36/36
├── Image Optimization: 27/27
├── Reading Time: 33/33
├── Social Share: 22/22
├── AI Metadata: 35/35
├── RSS Feed: 25/25
├── Schema: 28/28
└── Sitemap: 20/20
INTEGRATION TESTS: 15/15 tests passing (100%)
├── SEO Integration: 8/8
├── Theme Integration: 4/4
└── Performance Integration: 3/3
PRODUCTION TESTS: 12/15 tests passing (80%)
├── Meta Tags: 8/8
├── Schema.org: 3/3
├── Performance: 1/4 ️ (minor issues)
TOTAL: 305/332 tests passing (92% success rate)
Performance Impact
Test Execution Time: 45 seconds total
├── Unit Tests: 15 seconds
├── Integration Tests: 10 seconds
├── Production Tests: 20 seconds
Coverage: 85% code coverage
CI/CD: 3 minutes total pipeline
Zero False Positives: Tests are reliable
Workflow de Desarrollo
Test-Driven Development
# 1. Escribir test primero
npm run test:watch src/features/new-feature
# 2. Implementar feature
# ... código ...
# 3. Validar que pasa
npm run test:unit
# 4. Test de integración
npm run test:integration
# 5. Build y test de producción
npm run build
npm run test:seo:production
Conclusión
El testing automatizado transforma el desarrollo web:
- Confianza: Deploy sin miedo a romper algo
- Velocidad: Validación en minutos vs horas
- Calidad: Estándares consistentes siempre
- Escalabilidad: Funciona con cualquier tamaño de sitio
- Documentación: Tests como especificación viva
Con 305 tests automáticos, cada cambio se valida completamente, garantizando que el sitio mantenga la máxima calidad en SEO, performance y accesibilidad.
¿Estás listo para automatizar tu testing? ¡El setup completo está disponible y es completamente reutilizable!