Quando um teste Playwright falha com net::ERR_CONNECTION_REFUSED, esse erro diz exatamente qual camada da pilha de rede quebrou: TCP. Significa que nada estava ouvindo naquela porta quando o teste tentou se conectar. Os quatro passos que toda requisição percorre são lookup de DNS, conexão TCP, handshake TLS e troca HTTP, e cada camada tem sua própria assinatura de falha.
O que acontece quando o Playwright abre uma página
Quando seu teste roda await page.goto('https://lab.becomeqa.com'), quatro coisas acontecem em sequência:
1. Lookup de DNS
Seu computador pergunta a um servidor DNS: "Qual é o endereço IP de lab.becomeqa.com?"
O servidor DNS responde com algo como 104.21.8.42. Nomes de domínio são aliases legíveis por humanos. A rede real usa endereços IP.
ERR_NAME_NOT_RESOLVED: o DNS não encontrou o domínio. O domínio não existe, o servidor DNS está fora, ou (no CI) o container não consegue alcançar o DNS externo.- Em ambientes de CI, hostnames internos como
api.internalprecisam da rede do CI configurada corretamente para o DNS funcionar.
2. Conexão TCP
Seu computador abre uma conexão TCP para aquele endereço IP em um número de porta.
- Porta 443 = HTTPS (criptografado)
- Porta 80 = HTTP (sem criptografia)
- Porta 3000, 8080, etc. = servidores de desenvolvimento
ERR_CONNECTION_REFUSED: nada está ouvindo naquela porta. O servidor não está rodando ou você está acessando a porta errada.ERR_CONNECTION_TIMED_OUT: o servidor está inacessível (firewall, rede errada, servidor fora).- No CI, isso frequentemente significa que o servidor da aplicação não terminou de iniciar antes do teste rodar.
3. Handshake TLS (somente HTTPS)
Navegador e servidor trocam certificados e negociam a criptografia. Esse é o "S" do HTTPS.
O que quebra aqui:ERR_CERT_AUTHORITY_INVALID: o certificado SSL é auto-assinado ou expirou. Comum em ambientes de staging.ERR_SSL_PROTOCOL_ERROR: incompatibilidade de versão TLS ou configuração incorreta.- O Playwright tem
ignoreHTTPSErrors: trueno config para pular a validação de certificado em ambientes de teste:
// playwright.config.ts
use: {
ignoreHTTPSErrors: true, // para staging com certs auto-assinados
}4. Requisição e resposta HTTP
Agora acontece a transferência de conteúdo real. O navegador envia uma requisição; o servidor devolve uma resposta.
Uma requisição fica assim (simplificado):
GET /dashboard HTTP/1.1
Host: lab.becomeqa.com
Cookie: session=abc123
Accept: text/htmlUma resposta fica assim:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 4521
<!DOCTYPE html>...O status code diz o que aconteceu.
Status codes HTTP que você vai encontrar
| Código | Significado | Causa comum |
|--------|------------|-------------|
| 200 | OK | Tudo funcionou |
| 201 | Created | POST bem-sucedido, recurso foi criado |
| 204 | No Content | Sucesso, sem corpo (comum para DELETE) |
| 301/302 | Redirect | Página movida, navegador segue automaticamente |
| 400 | Bad Request | Requisição malformada: formato errado, campo faltando |
| 401 | Unauthorized | Não autenticado: precisa fazer login |
| 403 | Forbidden | Autenticado mas sem permissão: role/permissões erradas |
| 404 | Not Found | Recurso não existe naquele caminho |
| 422 | Unprocessable Entity | Formato válido mas conteúdo falhou na validação |
| 429 | Too Many Requests | Rate limit atingido |
| 500 | Internal Server Error | Bug no código do servidor |
| 502 | Bad Gateway | Servidor recebeu resposta ruim de um serviço upstream |
| 503 | Service Unavailable | Servidor fora ou sobrecarregado |
| 504 | Gateway Timeout | Serviço upstream demorou demais para responder |
Por que isso importa para os testes:Um 401 é um bug diferente de um 403. "Não está logado" vs "logado mas sem autorização" parecem idênticos na UI (ambos redirecionam para o login ou mostram um erro), mas precisam de correções diferentes.
Um 422 significa que a requisição chegou ao servidor e foi entendida, mas os dados falharam na validação. Um 400 significa que o servidor nem conseguiu interpretar a requisição.
Métodos HTTP: o que cada um significa
Toda requisição usa um método que descreve a operação pretendida:
| Método | Uso | Idempotente? |
|--------|-----|-------------|
| GET | Ler dados, carregar uma página, listar registros | Sim. Chamar duas vezes retorna o mesmo resultado |
| POST | Criar, enviar formulário, adicionar registro | Não. Chamar duas vezes cria dois registros |
| PUT | Substituir, atualizar um recurso inteiro | Sim. Chamar duas vezes tem o mesmo resultado |
| PATCH | Atualização parcial, alterar um campo | Geralmente sim |
| DELETE | Remover um recurso | Sim. Deletar duas vezes tem o mesmo resultado |
Por que isso importa para os testes:Se o seu teste chama POST duas vezes (por exemplo, o clique em um botão é registrado duas vezes), você deve obter dois registros no banco. Se você está testando a idempotência de um endpoint PUT, chamar duas vezes deve deixar o sistema no mesmo estado que chamar uma vez.
Quando você vê um bug onde "o formulário foi enviado duas vezes," você precisa saber se foram duas requisições POST ou uma. Isso é visível na aba Network do DevTools.
Headers: metadados em toda requisição e resposta
Headers carregam metadados. Não são o conteúdo principal, mas controlam como a requisição e a resposta são tratadas.
Headers de requisição que você vai ver nos testes:Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... ← token de autenticação
Content-Type: application/json ← "estou enviando JSON"
Cookie: session=abc123; csrf_token=xyz ← cookies de sessão
Accept: application/json ← "quero JSON de volta"Content-Type: application/json; charset=utf-8 ← resposta é JSON
Set-Cookie: session=abc123; HttpOnly; Secure ← servidor define um cookie
Cache-Control: no-store ← não armazene em cache
Location: /dashboard ← destino do redirect (com 302)test('API returns JSON content type', async ({ request }) => {
const response = await request.get('https://lab.becomeqa.com/api/items');
expect(response.status()).toBe(200);
expect(response.headers()['content-type']).toContain('application/json');
});Cookies e sessões: como o "logado" funciona
Quando você faz login, o servidor cria uma sessão e devolve um cookie:
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=StrictO navegador armazena esse cookie e o envia com cada requisição subsequente:
Cookie: session_id=abc123O servidor lê o cookie, busca a sessão e sabe quem você é.
Por que isso importa para os testes:É por isso que o storageState do Playwright funciona. Ele salva os cookies de uma sessão autenticada em um arquivo, depois os carrega em novos contextos de navegador. O servidor vê o mesmo cookie, acha que você ainda está logado e pula a autenticação.
// Salvar o estado de auth após o login
await page.context().storageState({ path: 'auth.json' });
// Carregar nos testes
use: {
storageState: 'auth.json'
}Quando um teste falha com "redirecionado para a página de login," significa que o cookie de sessão expirou, não foi salvo corretamente ou o arquivo auth.json está desatualizado.
Lendo o tráfego de rede no Playwright
O Playwright consegue interceptar e inspecionar toda requisição que o teste faz:
test('login sends correct credentials', async ({ page }) => {
// Ouvir a chamada da API de login
const loginRequest = page.waitForRequest('**/api/auth/login');
await page.goto('/');
await page.getByRole('button', { name: 'Login' }).click();
await page.getByLabel('Username').fill('admin@becomeqa.com');
await page.getByLabel('Password').fill('testpass123');
await page.getByRole('button', { name: 'Submit' }).click();
const request = await loginRequest;
const body = request.postDataJSON();
expect(body.email).toBe('admin@becomeqa.com');
});E respostas:
test('items API returns correct data structure', async ({ page }) => {
// Interceptar a chamada de API feita quando a página carrega
const itemsResponse = page.waitForResponse('**/api/items');
await page.goto('/dashboard');
const response = await itemsResponse;
expect(response.status()).toBe(200);
const items = await response.json();
expect(Array.isArray(items)).toBeTruthy();
});Padrões comuns de falha e o que significam
"net::ERR_CONNECTION_REFUSED" no CI mas não localmenteO servidor da aplicação não está rodando quando o teste começa. Adicione uma espera para o servidor estar pronto, ou use a config webServer no playwright.config.ts:
webServer: {
command: 'npm start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
}Sessão expirada ou estado de auth não carregado. Verifique se o storageState está configurado, ou se o setup global está rodando antes dos testes.
O teste está atingindo um rate limiter. Adicione delays entre requisições ou desative o rate limiting em ambientes de teste.
"Response timeout: 30000ms exceeded"O servidor demorou mais de 30 segundos para responder. Aumente o timeout para aquela operação específica, ou investigue por que o servidor está lento (query de banco, chamada de API externa, etc.).
Teste passa localmente, falha no CI com erro 500Variável de ambiente faltando no CI: URL do banco, chave de API, feature flag. O servidor lança um erro não tratado porque um valor de config necessário está indefinido.
A aba Network no DevTools
Ao debugar uma falha, abra o DevTools do Chrome na aba Network antes de rodar o fluxo manualmente. Toda requisição aparece com:
- URL e método
- Status code
- Headers e corpo da requisição
- Headers e corpo da resposta
- Timing
Isso diz exatamente o que o navegador enviou e o que o servidor retornou. Se a UI mostra um erro mas a aba Network mostra um 200, o bug está no JavaScript do frontend interpretando a resposta. Se a aba Network mostra um 500, o bug está no backend.
O trace viewer do Playwright mostra as mesmas informações para runs de testes automatizados. Abra o trace, mude para a aba Network e veja todas as requisições feitas durante o teste.
O que você não precisa saber (ainda)
- WebSockets e Server-Sent Events: conexões em tempo real, relevantes apenas para testar features em tempo real
- HTTP/2 e HTTP/3: versões de protocolo; o Playwright lida com elas de forma transparente
- Gerenciamento de certificados TLS: relevante para DevOps, não para QA do dia a dia
- Balanceamento de carga e configuração de CDN: conhecimento de infraestrutura para cargos sênior
- Detalhes do fluxo OAuth 2.0: útil ao testar features de auth especificamente
FAQ
Preciso entender redes para escrever testes Playwright?Não em profundidade. Mas entender o fluxo de quatro passos (DNS → TCP → TLS → HTTP) permite diagnosticar a classe do erro em 30 segundos em vez de 30 minutos. Você não precisa implementar nada disso. Só precisa reconhecer qual camada quebrou.
Devo verificar a aba Network ou a mensagem de erro do teste quando um teste falha?Os dois, nessa ordem. A mensagem de erro do teste diz o que a assertion falhou. A aba Network (ou o trace do Playwright) diz por quê: o que o servidor realmente retornou. Mensagem de erro primeiro, rede segundo.
Qual a diferença entre 401 e 403?401 = não autenticado (você não fez login). 403 = autenticado mas não autorizado (você está logado, mas não tem permissão para esse recurso). Ambos podem resultar em uma resposta "você não pode acessar isso" na UI, mas são bugs diferentes com correções diferentes.
→ Veja também: Testes de API com Playwright: Além da Interface | SQL para QA: As Consultas que Você Realmente Precisa