emailr_
Todos los artículos
usecase·10 min

Pruebas de email en CI/CD: patrones de automatización

testingcicdautomation

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

  1. Prueba temprano - Valida las plantillas en cada commit
  2. Snapshots con cuidado - Actualiza los snapshots intencionalmente
  3. Revisa la puntuación de spam - Detecta problemas antes de que afecten la entregabilidad
  4. Valida los enlaces - Los enlaces rotos perjudican la experiencia de usuario
  5. Prueba la accesibilidad - Asegura que los emails funcionen para todos
  6. 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.

e_

Escrito por el equipo de emailr

Construyendo infraestructura de email para desarrolladores

¿Listo para empezar a enviar?

Obtén tu clave API y envía tu primer email en menos de 5 minutos. No se requiere tarjeta de crédito.