Las pruebas automatizadas de email detectan problemas antes de que lleguen a producción. Así puedes integrar las pruebas de email en tu pipeline de CI/CD.
Qué probar
- —Sintaxis de plantillas y renderizado
- —Sustitución de variables
- —Validación de enlaces
- —Comprobación de la puntuación de spam
- —Cumplimiento de accesibilidad
- —Renderizado entre clientes
Validación de plantillas
Comprobación de sintaxis
// In your test suite
import { validateTemplate } from './email-utils';
describe('Email Templates', () => {
const templates = glob.sync('templates/**/*.html');
templates.forEach(templatePath => {
it(`${templatePath} should have valid syntax`, () => {
const content = fs.readFileSync(templatePath, 'utf-8');
const result = validateTemplate(content);
expect(result.valid).toBe(true);
expect(result.errors).toEqual([]);
});
});
});
function validateTemplate(html: string): ValidationResult {
const errors: string[] = [];
// Check for unclosed tags
const unclosedTags = findUnclosedTags(html);
if (unclosedTags.length > 0) {
errors.push(`Unclosed tags: ${unclosedTags.join(', ')}`);
}
// Check for invalid variables
const invalidVars = findInvalidVariables(html);
if (invalidVars.length > 0) {
errors.push(`Invalid variables: ${invalidVars.join(', ')}`);
}
return { valid: errors.length === 0, errors };
}
Validación de variables
it('should have all required variables', () => {
const template = loadTemplate('order-confirmation');
const requiredVars = ['orderNumber', 'items', 'total', 'customerName'];
const templateVars = extractVariables(template);
for (const required of requiredVars) {
expect(templateVars).toContain(required);
}
});
Pruebas de renderizado
Pruebas con snapshots
import { render } from './email-renderer';
describe('Email Rendering', () => {
it('renders order confirmation correctly', async () => {
const html = await render('order-confirmation', {
orderNumber: 'ORD-123',
customerName: 'Test User',
items: [{ name: 'Widget', quantity: 2, price: 29.99 }],
total: 59.98
});
expect(html).toMatchSnapshot();
});
});
Pruebas de regresión visual
import { takeScreenshot, compareImages } from './visual-testing';
it('should match visual baseline', async () => {
const html = await render('welcome', testData);
const screenshot = await takeScreenshot(html);
const diff = await compareImages(screenshot, 'welcome-baseline.png');
expect(diff.percentage).toBeLessThan(0.1); // 0.1% tolerance
});
Validación de enlaces
Comprobar todos los enlaces
import { extractLinks, validateUrl } from './link-utils';
describe('Email Links', () => {
const templates = glob.sync('templates/**/*.html');
templates.forEach(templatePath => {
it(`${templatePath} should have valid links`, async () => {
const html = fs.readFileSync(templatePath, 'utf-8');
const links = extractLinks(html);
for (const link of links) {
// Skip template variables
if (link.includes('{{')) continue;
const isValid = await validateUrl(link);
expect(isValid).toBe(true);
}
});
});
});
Verificación del enlace para cancelar la suscripción
it('marketing emails should have unsubscribe link', () => {
const marketingTemplates = glob.sync('templates/marketing/**/*.html');
marketingTemplates.forEach(template => {
const html = fs.readFileSync(template, 'utf-8');
expect(html).toMatch(/unsubscribe/i);
expect(html).toContain('List-Unsubscribe');
});
});
Comprobación de la puntuación de spam
Integración con SpamAssassin
import { checkSpamScore } from './spam-checker';
describe('Spam Score', () => {
it('should have acceptable spam score', async () => {
const html = await render('newsletter', testData);
const result = await checkSpamScore({
from: '[email protected]',
subject: 'Your weekly update',
html
});
expect(result.score).toBeLessThan(5.0);
expect(result.flags).not.toContain('MISSING_HEADERS');
});
});
Pruebas de accesibilidad
import { checkAccessibility } from './a11y-checker';
describe('Email Accessibility', () => {
it('should pass accessibility checks', async () => {
const html = await render('welcome', testData);
const results = await checkAccessibility(html);
expect(results.violations).toHaveLength(0);
});
it('should have alt text on images', () => {
const html = fs.readFileSync('templates/welcome.html', 'utf-8');
const images = html.match(/<img[^>]*>/g) || [];
images.forEach(img => {
expect(img).toMatch(/alt=["'][^"']+["']/);
});
});
});
Integración en el pipeline de CI/CD
Ejemplo con GitHub Actions
name: Email Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Validate templates
run: npm run test:templates
- name: Check spam scores
run: npm run test:spam
- name: Visual regression tests
run: npm run test:visual
- name: Upload visual diffs
if: failure()
uses: actions/upload-artifact@v4
with:
name: visual-diffs
path: test-results/visual-diffs/
Comprobaciones previas al despliegue
Pruebas en el entorno de staging
// Send test emails to staging inbox
async function testInStaging() {
const testEmails = [
{ template: 'welcome', data: welcomeTestData },
{ template: 'order-confirmation', data: orderTestData },
{ template: 'password-reset', data: resetTestData }
];
for (const { template, data } of testEmails) {
await sendEmail({
to: '[email protected]',
template,
data,
tags: ['staging-test', `template:${template}`]
});
}
// Verify delivery
await sleep(5000);
const delivered = await checkStagingInbox();
expect(delivered).toHaveLength(testEmails.length);
}
Buenas prácticas
- —Prueba temprano - Valida las plantillas en cada commit
- —Snapshots con cuidado - Actualiza los snapshots intencionalmente
- —Revisa la puntuación de spam - Detecta problemas antes de que afecten la entregabilidad
- —Valida los enlaces - Los enlaces rotos perjudican la experiencia de usuario
- —Prueba la accesibilidad - Asegura que los emails funcionen para todos
- —Usa staging - Pruebas de entrega reales antes de producción
Las pruebas automatizadas de email detectan problemas antes de que los usuarios los vean. Invierte en tu suite de pruebas y despliega con confianza.