Grave e reproduza chamadas de API em testes do Playwright, incluindo requisições do lado do servidor

Grave uma vez.
Reproduza para sempre.

Grava as respostas da API que sua aplicação recebe durante uma execução local do Playwright, incluindo WebSockets, e depois as reproduz na CI com o seu backend desligado.

npm i -D test-proxy-recorder Estrelar no GitHub

v2.0.1 · MIT · TypeScript · Node ≥ 20 · SSR de Next.js e TanStack Start, SPAs, extensões do Chrome, WebSockets

REC · MODE=record · local run

saved todos__adds-a-todo.mock.json

REPLAY · MODE=replay · CI

200 OK · 3 ms · from disk

Veja gravar e depois reproduzir

Uma execução do Playwright grava respostas reais no disco; mude para reproduzir e a mesma suite passa com o backend desligado.

Duas formas de gravar

As requisições se originam em dois lugares, então há duas formas de gravá-las, e o processo do proxy roda em ambas. Tudo o que é enviado ao proxy (buscas do lado do servidor, chamadas do navegador por uma URL base da API compartilhada com o servidor, WebSockets) é reproduzido a partir do .mock.json do teste. As chamadas do navegador para outros hosts que correspondam ao padrão de url da sua fixture são reproduzidas a partir do .har do teste.

Proxy

.mock.json

SSR de Next.js / TanStack Start → proxy → API real

Para aplicações full-stack onde o servidor chama a API.

Fica entre o seu servidor e a API. Grava requisições do lado do servidor: buscas de Server Components e de SSR, route handlers, tudo o que o seu backend-for-frontend chama.

As requisições do lado do servidor de cada teste carregam o id desse teste, então testes rodando em paralelo nunca compartilham uma gravação.

HAR

.har

navegador → interceptação HAR → API real

Para SPAs, extensões e aplicações somente de navegador.

Intercepta no próprio navegador e salva um arquivo HTTP Archive (HAR). Grava chamadas fetch do lado do cliente, tráfego de API de extensões do Chrome, analytics e APIs de terceiros, desde que correspondam ao padrão de url da sua fixture.

Em comparação com o routeFromHAR puro, ele acrescenta um HAR por teste e remove os valores dos headers de autenticação.

Como é uma gravação

Um arquivo por teste
Cada teste escreve seu próprio arquivo, com o nome do teste, então npx playwright test -g "adds a todo" grava de novo só esse teste. O proxy grava um teste por vez, então grave com um único worker; a reprodução roda em paralelo.
O id do teste
O Playwright envia x-test-rcrd-id em cada requisição que um teste faz, e o registerProxyFetch() o copia para as buscas feitas pelo seu próprio servidor. O proxy o lê para responder a cada teste a partir do próprio arquivo desse teste, mesmo quando os testes são reproduzidos em paralelo.
Segredos
Os headers Authorization, Cookie e Set-Cookie passam a ser [REDACTED] nos arquivos .mock.json antes de serem salvos, e nos arquivos .har quando a execução termina. Segredos dentro dos corpos de resposta exigem padrões definidos por você. Remoção de segredos →
e2e/recordings/todos__adds-a-todo.mock.json
{
"id": "todos__adds-a-todo",
"recordings": [
{
"request": {
"method": "POST",
"url": "/todos",
"headers": {
"authorization": "[REDACTED]",
"x-test-rcrd-id": "todos__adds-a-todo"
},
"body": "{\"title\":\"Buy groceries\"}"
},
"response": {
"statusCode": 201,
"body": "{\"id\":7,\"title\":\"Buy groceries\",\"done\":false}"
},
"key": "POST_todos.json",
"sequence": 0
}
]
}
Parte de uma gravação real, 2.6 KB no total: o POST de um teste, com o valor do header Authorization removido. O arquivo completo também contém o GET que veio em seguida, todos os headers e um timestamp para cada requisição.

Onde ele se encaixa

As ferramentas de mock são boas em trabalhos diferentes. A combinação abaixo — gravar tráfego real em SSR, navegador e WebSockets, sem mocks escritos à mão — é a lacuna que as outras deixam em aberto.

Comparação de recursos do test-proxy-recorder com o routeFromHAR do Playwright, MSW, Polly.js, playwright-network-cache e Mocky Balboa.
Recursotest-proxy-recorderPlaywright routeFromHARMSWPolly.jsplaywright-network-cacheMocky Balboa
Grava tráfego realSimSimNãoSimSimNão
Lado do servidor (SSR)SimNãoSimParcialNãoSim
Lado do navegadorSimSimSimSimSimSim
WebSocketSimNãoSimNãoNãoNão
Nativo do PlaywrightSimSimSimNãoSimSim
MantidoSimSimSimNãoSimSim

O Polly.js grava dentro do processo em que roda, então, para requisições do lado do servidor, ele precisa rodar dentro do servidor da sua aplicação. O MSW e o Mocky Balboa também mockam requisições do lado do servidor, mas você mesmo escreve as respostas. A comparação completa, com fontes e quando escolher outra ferramenta, está na documentação.

A contrapartida é uma primeira configuração mais trabalhosa do que a das ferramentas somente de navegador: um processo de proxy roda ao lado dos seus testes e, para requisições do lado do servidor, a URL base da API da sua aplicação aponta para ele durante as execuções de teste.

Funciona com o seu provedor de autenticação real

Faça login via Cognito, Auth0, Clerk ou WorkOS — de verdade, em cada execução. Somente a API da sua aplicação é gravada; a autenticação permanece ao vivo e seus dados ficam offline.

// e2e/auth.setup.ts — log in for real, once. Never recorded.
import { test as setup } from '@playwright/test';
import { setProxyMode } from 'test-proxy-recorder';
setup('authenticate', async ({ page }) => {
await setProxyMode('transparent'); // login bypasses the recorder
await page.goto('/login');
await page.getByTestId('email').fill(process.env.TEST_EMAIL!);
await page.getByTestId('password').fill(process.env.TEST_PASSWORD!);
await page.getByTestId('signinButton').click();
await page.waitForURL('/dashboard');
// Reused by every test — they start already signed in.
await page.context().storageState({ path: 'e2e/.auth/state.json' });
});

Início rápido

Gere tudo com um comando, aponte sua API para o proxy, depois grave e faça commit. Aplicação somente de navegador? init pula a etapa de SSR para você.

O que muda no seu repositório

  • Na sua suite de testes: uma fixture do Playwright, o processo do proxy (ele só roda durante os testes) e gravações commitadas no git.
  • Na sua aplicação, só para requisições do lado do servidor: aponte a URL base da API para o proxy durante as execuções de teste e chame registerProxyFetch() uma vez, em app/layout.tsx no Next.js ou em src/router.tsx no TanStack Start. Ele copia o id do teste para cada busca do lado do servidor e não faz nada em produção, a menos que TEST_PROXY_RECORDER_ENABLED esteja definido.
  • Com o prompt do agente: o @tanstack/intent adiciona ao arquivo de config do seu agente, como o CLAUDE.md, orientações para carregar as skills desta biblioteca.
  • Testando um ambiente implantado? O HAR grava as chamadas do navegador desse ambiente sem nenhuma mudança na aplicação, mas as próprias páginas continuam sendo carregadas daquele servidor. Para reproduzir também as chamadas do lado do servidor, rode a aplicação na CI ao lado do proxy.

Cole isto no Claude Code, no Cursor ou em outro agente de codificação. Primeiro ele executa o @tanstack/intent, que adiciona orientações ao arquivo de config do seu agente, como o CLAUDE.md, para que o agente carregue as skills de configuração desta biblioteca. Depois ele instala o pacote, encontra a URL do seu backend na config da aplicação, executa o init e conclui a configuração a partir da saída desse comando.

Set up test-proxy-recorder for end-to-end tests in this project, then follow the instructions that `init` prints. Run these commands:
npx @tanstack/intent@latest install
npm install --save-dev test-proxy-recorder
Then run init, passing this project's backend API base URL as the target — find it yourself from the app's env/config (the URL the app calls in dev); don't assume the default:
npx test-proxy-recorder init <your-backend-api-url> --port 8100 --dir ./e2e/recordings
Then complete the app-specific steps init prints: point the app's API base URL at the proxy in dev/test only, tag server-side fetches (Next.js), add a smoke test, and verify record → replay.

Guia completo de configuração →

Perguntas sobre a reprodução

Como é feita a correspondência das requisições?
O proxy faz a correspondência de uma chamada do lado do servidor pelo método, pelo caminho e pela query string exata, não pelo corpo, e serve as chamadas repetidas na ordem em que foram gravadas. As chamadas do navegador seguem as regras de HAR do Playwright, que também comparam os corpos de POST. Uma query string que muda a cada execução, como um timestamp, nunca corresponde, e nenhuma opção ignora um parâmetro, então mantenha esses valores fixos durante os testes. Correspondência de requisições na reprodução →
E se faltar uma gravação?
O proxy responde 404 e identifica a requisição e o teste. Uma chamada feita mais vezes do que foi gravada recebe de novo a última resposta, com um aviso no log do proxy. No navegador, o Playwright aborta uma chamada que corresponde ao padrão url que sua fixture passa para before() mas não está no HAR; as outras chamadas chegam à rede.
Quando a reprodução chega à API real?
A reprodução só chega à API real depois que o proxy sai do modo replay, e o proxy mantém um único modo para todos os testes em execução. Ele sai do modo replay 120 segundos depois do início do último teste, ou quando um hook por teste chama teardown(). Sem backend rodando na CI, uma chamada assim falha em vez de passar. Timeout da sessão e teardown →
O que acontece quando a API muda?
Com um cliente de API tipado, atualizar o cliente por causa de um campo alterado faz falhar os testes reproduzidos que o usam, porque as gravações ainda contêm o campo antigo. Isso aponta para a mudança, e gravar de novo corrige os testes com menos trabalho do que editar mocks escritos à mão.
Como testo erros e casos-limite?
No navegador, um page.route() registrado depois de before() sobrescreve a gravação. As respostas do lado do servidor são editadas no arquivo .mock.json. Forçar uma resposta →

Pare de escrever mocks à mão

Sua API já dá as respostas certas. Grave-as.

npm i -D test-proxy-recorder Estrelar no GitHub

Se isso economizou uma tarde, uma estrela leva um segundo — é assim que a próxima pessoa encontra e diz a um mantenedor solo para continuar construindo. Esbarrou em um problema ou teve uma ideia? Abra uma issue ou entre no Discord.