Envolver uma assertion do Playwright em try/catch silencia a falha: o teste passa quando não deveria, e o bug vai para produção. O finally garante limpeza mesmo quando o teste falha. Mensagens throw customizadas tornam erros de setup mais fáceis de diagnosticar do que falhas genéricas de assertion.

O básico: try/catch

Um bloco try/catch permite rodar código e tratar erros sem travar o programa:

try {
  // Código que pode lançar um erro
  const resultado = JSON.parse('json inválido');
} catch (error) {
  // Isso roda se o bloco try lançar um erro
  console.log('Parsing falhou:', error.message);
}

Sem try/catch, JSON.parse('json inválido') travaria o script com um erro não tratado. Com try/catch, você trata com controle.

A variável error no bloco catch é o objeto Error, que tem:

  • error.message — descrição legível
  • error.name — tipo do erro ('SyntaxError', 'TypeError', etc.)
  • error.stack — stack trace completo

O bloco finally

finally roda independente de ocorrer um erro ou não: útil para limpeza.

test('upload de arquivo com limpeza', async ({ page }) => {
  const arquivoTemp = await criarArquivoTemp();
  
  try {
    await page.setInputFiles('[data-testid="upload"]', arquivoTemp);
    await expect(page.locator('[data-testid="upload-success"]')).toBeVisible();
  } catch (error) {
    console.error('Teste de upload falhou:', error.message);
    throw error; // Relança para que o teste ainda falhe
  } finally {
    await deletarArquivoTemp(arquivoTemp); // Sempre executa, mesmo em falha
  }
});

finally sempre roda. Valioso para código de limpeza em testes.

async/await e erros

No Playwright, quase tudo é async. Erros em código assíncrono funcionam da mesma forma, você só precisa do await no lugar certo:

// Sem try/catch: rejeição não tratada = teste falha com erro feio
async function getUsuarioDaApi() {
  const response = await fetch('/api/user/999');
  const data = await response.json();
  return data;
}

// Com try/catch: rejeição tratada = você controla a mensagem de erro
async function getUsuarioDaApi() {
  try {
    const response = await fetch('/api/user/999');
    if (!response.ok) {
      throw new Error(`Erro da API: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Falha ao buscar usuário:', error.message);
    throw error; // Relança se quiser que o chamador trate também
  }
}

Lançando seus próprios erros

Você pode lançar erros intencionalmente para sinalizar falha:

function validarEmail(email: string): void {
  if (!email.includes('@')) {
    throw new Error(`Formato de e-mail inválido: "${email}"`);
  }
  if (email.length > 254) {
    throw new Error(`E-mail muito longo: ${email.length} caracteres (máx. 254)`);
  }
}

try {
  validarEmail('nao-e-email');
} catch (error) {
  console.log(error.message); // 'Formato de e-mail inválido: "nao-e-email"'
}

Em testes, lançar é como você falha com uma mensagem específica em vez de uma falha genérica de assertion:

async function getTokenAuth(request: APIRequestContext): Promise<string> {
  const response = await request.post('/api/auth/login', {
    data: { email: 'teste@teste.com', password: 'SenhaValida1' },
  });
  
  if (response.status() !== 200) {
    throw new Error(`Login falhou: ${response.status()} — não é possível prosseguir com os testes`);
  }
  
  const { token } = await response.json();
  if (!token) {
    throw new Error('Resposta do login sem campo token');
  }
  
  return token;
}

Tipos de erro

O JavaScript tem vários tipos de erro embutidos:

| Tipo de erro | Quando aparece |

|-------------|---------------|

| Error | Erro genérico (classe base) |

| TypeError | Tipo errado: null.algo, chamar não-função |

| SyntaxError | JavaScript inválido ou parsing de JSON |

| RangeError | Valor fora do range: new Array(-1) |

| ReferenceError | Variável não existe |

Você pode verificar o tipo para tratar erros diferentes de formas diferentes:

try {
  await fazerAlgo();
} catch (error) {
  if (error instanceof TypeError) {
    console.log('TypeError — verifique seus tipos de dados');
  } else if (error instanceof SyntaxError) {
    console.log('SyntaxError — JSON inválido ou similar');
  } else {
    throw error; // Erro desconhecido — relança
  }
}

Padrões comuns em testes Playwright

1. Tentando novamente uma operação após falha

async function aguardarApiPronta(request: APIRequestContext, maxTentativas = 5): Promise<void> {
  for (let i = 0; i < maxTentativas; i++) {
    try {
      const response = await request.get('/api/health');
      if (response.status() === 200) return;
    } catch (error) {
      // API ainda não está pronta, tente novamente
    }
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
  throw new Error(`API não ficou pronta após ${maxTentativas} tentativas`);
}

2. Coletando erros sem parar a execução

const erros: string[] = [];

for (const email of emailsDeTeste) {
  try {
    await validarCampoEmail(page, email);
  } catch (error) {
    erros.push(`${email}: ${error.message}`);
  }
}

if (erros.length > 0) {
  throw new Error(`Falhas de validação:\n${erros.join('\n')}`);
}

3. Classe de erro customizada para código mais limpo

class TestDataError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'TestDataError';
  }
}

// Agora você consegue distinguir erros de setup de dados dos erros de assertion
try {
  const usuario = await criarUsuarioTeste(request);
} catch (error) {
  if (error instanceof TestDataError) {
    console.error('Setup do teste falhou — pulando teste');
    test.skip();
  }
  throw error;
}

Erros no Playwright: o que você vai ver

TimeoutError

O erro mais comum no Playwright. Um locator não ficou visível a tempo:

TimeoutError: locator.click: Timeout 30000ms exceeded.
Locator: [data-testid="submit-button"]

Esse não é um erro JavaScript que você precisa capturar na maioria dos casos. O Playwright trata e falha o teste com uma mensagem clara. Mas se você quiser tratar um timeout graciosamente (tentar outra coisa se um elemento não aparecer), pode:

try {
  await page.locator('[data-testid="popup"]').click({ timeout: 2000 });
} catch (error) {
  // Popup não apareceu em 2 segundos — tudo bem
  // Continua o teste sem interagir com o popup
}

Rejeição de Promise não tratada

Se você chama uma função async sem await e ela lança um erro, pode virar uma "unhandled rejection": não falha o teste imediatamente e a mensagem de erro pode confundir.

// Ruim: sem await, o erro pode ser engolido
page.goto('/admin');  // Se isso falhar, o teste pode passar com estado errado

// Bom: o erro aparece imediatamente
await page.goto('/admin');

Sempre use await em operações async nos testes.

Quando NÃO usar try/catch em testes

Não use try/catch para esconder falhas de assertion. Se uma assertion falhar, o teste deve falhar:

// Errado — engole a falha de assertion, teste "passa" quando não deveria
try {
  await expect(page.locator('[data-testid="success"]')).toBeVisible();
} catch (error) {
  console.log('Elemento não visível, mas continuando...');
}

// Correto — deixa falhas de assertion falharem o teste
await expect(page.locator('[data-testid="success"]')).toBeVisible();

try/catch em testes serve para:
  • Código de setup e teardown onde a falha não deve mascarar o teste real
  • Resiliência intencional (lógica de retry, interações opcionais)
  • Criação de dados de teste onde você quer uma mensagem de erro clara e customizada

Não serve para silenciar assertions.

Resumo rápido

  • try/catch trata erros: o código em try roda, catch trata qualquer erro lançado
  • finally sempre roda: use para limpeza
  • Sempre use await em operações async: falhas sem await são difíceis de debugar
  • Use throw intencionalmente quando quiser uma mensagem de erro específica
  • Não capture falhas de assertion: elas devem falhar o teste
  • instanceof permite verificar o tipo do erro e tratar erros diferentes de formas diferentes

Tratamento de erros é uma ferramenta para tornar o código mais resiliente e as mensagens de erro mais informativas, não para esconder falhas.

→ Veja também: JavaScript para QA Engineers: O Mínimo para Começar a Automatizar | Async/Await em Português Simples (para Testadores que se Perdem com Promises) | Como Ler Mensagens de Erro do Playwright