O teste de WebSocket com Playwright divide em dois padrões: observar conexões reais escutando os eventos framesent e framereceived. A alternativa é mockar o servidor com page.routeWebSocket() (Playwright 1.48+) para controlar quais mensagens o app recebe sem precisar de um backend real.
Como o teste de WebSocket funciona na prática
Diferente de requisições HTTP, WebSockets são conexões bidirecionais persistentes. Uma única conexão pode carregar milhares de mensagens ao longo da sua vida útil. Testar WebSockets significa:
1. Verificar que a conexão é estabelecida
2. Verificar que o app envia as mensagens certas
3. Verificar que o app trata corretamente as mensagens recebidas
4. Testar o que acontece quando a conexão cai
Escutando eventos de WebSocket
O Playwright dispara eventos para conexões WebSocket:
import { test, expect } from '@playwright/test';
test('app de chat estabelece conexão WebSocket', async ({ page }) => {
// Escuta por conexões WebSocket
const wsConnected = page.waitForEvent('websocket');
await page.goto('/chat');
const ws = await wsConnected;
expect(ws.url()).toContain('/ws/chat');
console.log('Conectado em:', ws.url());
});Capturando mensagens WebSocket
test('chat envia mensagem via WebSocket', async ({ page }) => {
const messages: string[] = [];
page.on('websocket', ws => {
ws.on('framesent', frame => {
// Mensagens enviadas do browser para o servidor
if (frame.text) messages.push(frame.text);
});
});
await page.goto('/chat');
await page.getByPlaceholder('Type a message').fill('Olá, mundo!');
await page.keyboard.press('Enter');
// Verifica que a mensagem WebSocket foi enviada
await expect.poll(() => messages).toContain(
JSON.stringify({ type: 'message', content: 'Olá, mundo!' })
);
});
test('chat recebe mensagem e a exibe', async ({ page }) => {
const receivedFrames: string[] = [];
page.on('websocket', ws => {
ws.on('framereceived', frame => {
// Mensagens recebidas do servidor pelo browser
if (frame.text) receivedFrames.push(frame.text);
});
});
await page.goto('/chat');
// Aguarda pelo menos uma mensagem chegar (ex: confirmação de entrada)
await expect.poll(() => receivedFrames.length).toBeGreaterThan(0);
// Verifica que a mensagem está exibida na UI
const firstMessage = JSON.parse(receivedFrames[0]);
await expect(page.getByText(firstMessage.content)).toBeVisible();
});framesent: mensagens que o browser enviou para o servidor.
framereceived: mensagens que o servidor enviou para o browser.
Mockando respostas WebSocket
O padrão mais poderoso: interceptar a conexão e simular mensagens do servidor sem rodar um servidor real.
O Playwright não tem uma API nativa de mock WebSocket, mas você pode injetar um mock no contexto do browser:
test('app exibe notificação quando servidor envia alerta', async ({ page }) => {
// Intercepta WebSocket e simula comportamento do servidor
await page.addInitScript(() => {
const OriginalWebSocket = window.WebSocket;
window.WebSocket = class MockWebSocket extends EventTarget {
url: string;
readyState = 1; // OPEN
constructor(url: string) {
super();
this.url = url;
// Simula abertura da conexão
setTimeout(() => {
this.dispatchEvent(new Event('open'));
// Simula servidor enviando uma notificação após 100ms
setTimeout(() => {
const messageEvent = new MessageEvent('message', {
data: JSON.stringify({ type: 'notification', text: 'Novo pedido recebido!' }),
});
this.dispatchEvent(messageEvent);
}, 100);
}, 0);
}
send(data: string) {
console.log('WebSocket send:', data);
}
close() {
this.readyState = 3;
this.dispatchEvent(new CloseEvent('close'));
}
} as any;
});
await page.goto('/dashboard');
// Verifica que a notificação aparece quando a mensagem WebSocket chega
await expect(page.getByRole('alert')).toBeVisible({ timeout: 2000 });
await expect(page.getByRole('alert')).toHaveText('Novo pedido recebido!');
});Testando desconexão e reconexão
Apps em tempo real precisam lidar com perda de conexão de forma adequada:
test('app mostra estado de reconexão quando WebSocket cai', async ({ page }) => {
await page.addInitScript(() => {
const OriginalWebSocket = window.WebSocket;
window.WebSocket = class extends OriginalWebSocket {
constructor(url: string) {
super(url);
// Expõe a instância para controle pelo teste
(window as any).__wsInstance = this;
}
} as any;
});
await page.goto('/chat');
// Aguarda a conexão ser estabelecida
await page.waitForEvent('websocket');
// Força o fechamento da conexão WebSocket
await page.evaluate(() => {
(window as any).__wsInstance?.close();
});
// Verifica que a UI mostra o estado de reconexão
await expect(page.getByText('Reconnecting...')).toBeVisible();
// Após algum tempo, verifica que tenta reconectar
await expect(page.getByText('Connected')).toBeVisible({ timeout: 10000 });
});Usando routeWebSocket nas versões mais recentes do Playwright
O Playwright 1.48+ introduziu page.routeWebSocket() para um mock de WebSocket mais ergonômico:
// Playwright 1.48+
test('status do pedido atualiza em tempo real', async ({ page }) => {
await page.routeWebSocket('/ws/orders', ws => {
ws.onopen = () => {
// Envia estado inicial
ws.send(JSON.stringify({ orderId: '123', status: 'processing' }));
};
ws.onmessage = (message) => {
const data = JSON.parse(message.data);
if (data.type === 'subscribe' && data.orderId === '123') {
// Simula progressão de status
setTimeout(() => ws.send(JSON.stringify({ orderId: '123', status: 'shipped' })), 500);
setTimeout(() => ws.send(JSON.stringify({ orderId: '123', status: 'delivered' })), 1000);
}
};
});
await page.goto('/orders/123');
await expect(page.getByTestId('status')).toHaveText('Processing');
await expect(page.getByTestId('status')).toHaveText('Shipped', { timeout: 2000 });
await expect(page.getByTestId('status')).toHaveText('Delivered', { timeout: 3000 });
});Verifique sua versão do Playwright antes de usar routeWebSocket: não está disponível em versões antigas.
Quando testar WebSockets
Nem toda feature em tempo real precisa de testes dedicados de WebSocket. Priorize:
- Estabelecimento de conexão para features críticas (chat, dashboard ao vivo)
- Comportamento quando a conexão cai (mostra erro? reconecta? perde dados?)
- Ordenação de mensagens para operações sequenciais
- Autenticação via WebSocket (a conexão é rejeitada sem auth válido?)
Pule testes no nível de WebSocket quando:
- A feature funciona de forma confiável e o teste E2E de UI já cobre isso
- O WebSocket é de terceiros (você não controla o servidor)
- Testar o formato da mensagem é melhor coberto por um teste unitário no backend