emailr_
Todos os artigos
usecase·10 min

Testes de email em CI/CD: Padrões de automação

testingcicdautomation

Testes automatizados de email detectam problemas antes de chegarem à produção. Veja como integrar testes de email ao seu pipeline de CI/CD.

O que testar

  • Sintaxe e renderização de templates
  • Substituição de variáveis
  • Validação de links
  • Verificação de pontuação de spam
  • Conformidade de acessibilidade
  • Renderização em diferentes clientes de email

Validação de templates

Verificação de sintaxe

// 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 };
}

Validação de variáveis

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);
  }
});

Testes de renderização

Testes de snapshot

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();
  });
});

Testes de regressão 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
});

Validação de links

Verificar todos os links

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);
      }
    });
  });
});

Verificação do link de cancelar inscrição

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');
  });
});

Verificação de pontuação de spam

Integração com 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');
  });
});

Testes de acessibilidade

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=["'][^"']+["']/);
    });
  });
});

Integração ao pipeline de CI/CD

Exemplo de 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/

Verificações pré-implantação

Testes em ambiente 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);
}

Boas práticas

  1. Teste cedo - Valide os templates a cada commit
  2. Snapshots com cuidado - Atualize os snapshots intencionalmente
  3. Verifique a pontuação de spam - Detecte problemas antes que afetem a entregabilidade
  4. Valide links - Links quebrados prejudicam a experiência do usuário
  5. Teste a acessibilidade - Garanta que os emails funcionem para todos
  6. Use staging - Testes de entrega reais antes da produção

Testes automatizados de email detectam problemas antes que os usuários os vejam. Invista na sua suíte de testes e faça deploy com confiança.

e_

Escrito pela equipe emailr

Construindo infraestrutura de email para desenvolvedores

Pronto para começar a enviar?

Obtenha sua chave API e envie seu primeiro email em menos de 5 minutos. Não é necessário cartão de crédito.