Módulo 5: Testes de API
O fixture request do Playwright, chamar API sem abrir navegador, autenticação com token, e test.describe.serial pro ciclo de vida de um recurso.
🎥 Prefere ver antes de ler?
Esse vídeo (6min10) explica o que é o fixture request, o que é um header e o
formato Bearer, de onde vem esse token (e de onde vem num projeto de verdade),
por que data vira JSON sozinho, por que
test.describe.serial existe (e por que os outros módulos nunca precisaram
dele), como res.json() lê o corpo da resposta, e o cuidado de limpar o que
você criou já que o site não tem banco de dados. Fecha mostrando o comando rodando de
verdade num terminal (repara: nenhuma janela de navegador abre) e criando/apagando um
produto direto pela API, refletido ao vivo na lista do site. Não substitui o texto abaixo,
mas ajuda a visualizar antes de praticar.
1. Por que testar API com o Playwright, se ele é de navegador
Os Módulos 1 a 4 abriram um navegador pra cada teste. Mas boa
parte do que vale testar numa API não precisa de tela nenhuma: criar um dado de
setup antes de um teste de UI, conferir que um endpoint devolve o status e o corpo
certos, ou testar um cenário que seria lento (ou frágil) de montar clicando na
página. O @playwright/test já vem com um fixture pra isso:
request. Ele faz chamada HTTP pura, sem Chromium, sem
page.goto, e aponta pro mesmo baseURL configurado em
playwright.config.js:
test("GET /api/produtos retorna 200", async ({ request }) => {
const res = await request.get("/api/produtos?id=1");
expect(res.status()).toBe(200);
});
Repare que o teste recebe request em vez de
page: são dois fixtures diferentes que o Playwright oferece, e você usa
o que o teste precisa (ou os dois juntos, como no desafio no fim deste módulo).
2. Autenticação com token
No QA Treino, ler produto é público, mas criar, editar e
excluir exigem um token fixo no header Authorization. É o cenário
mais comum de API em produção: leitura aberta, escrita protegida. Sem o header
certo, a API responde 401:
De onde vem esse token-treino-123? Aqui, de lugar nenhum dinâmico: ele
está escrito no README do QA Treino, só pra simplificar o aprendizado. Repare
que o /api/login deste site (o mesmo do Módulo 1) não devolve token nenhum,
só o nome da pessoa pra controlar a sessão da tela — são dois mecanismos
completamente separados. Numa API de verdade, o token quase nunca é um valor fixo
decorado: ele normalmente é gerado na hora, por um endpoint de login que devolve
um token novo a cada vez (ou por um painel de "API keys" do serviço), e costuma expirar
depois de um tempo. Se um dia você testar uma API assim, o primeiro passo do teste
costuma ser literalmente fazer esse login e guardar o token que veio na resposta, pra
usar nas chamadas seguintes.
Sobre deixar o token escrito no código: aqui é só pra simplificar o
aprendizado, mas num projeto de verdade isso é considerado vazamento de segredo —
o mesmo cuidado que o BASE_URL já recebe no Módulo 1
(process.env.BASE_URL || "..." no playwright.config.js).
Token, senha e chave de API seguem a mesma lógica: ficam numa variável de ambiente
(ou num .env fora do controle de versão) e o teste só lê
process.env.TOKEN, nunca o valor escrito direto no arquivo.
const TOKEN = "token-treino-123";
const AUTORIZADO = { Authorization: `Bearer ${TOKEN}` };
test("POST sem token retorna 401", async ({ request }) => {
const res = await request.post("/api/produtos", {
data: { nome: "Produto sem token", preco: 10 },
});
expect(res.status()).toBe(401);
});
test("POST com token cria produto", async ({ request }) => {
const res = await request.post("/api/produtos", {
headers: AUTORIZADO,
data: { nome: "Produto de teste", preco: 50, categoria: "Acessórios", emoji: "🧪" },
});
expect(res.status()).toBe(201);
});
O data vira o corpo da requisição em JSON
automaticamente (não precisa de JSON.stringify nem de
Content-Type manual). headers é onde entra qualquer header
extra, como o Authorization aqui.
3. test.describe.serial: testar o ciclo de vida de um recurso
Nos módulos anteriores, cada teste era independente de
propósito, o Playwright roda testes em paralelo e não garante ordem. Mas pra testar
criar → ler → atualizar → excluir o mesmo produto, os passos dependem um do
outro: o segundo precisa do id que o primeiro criou.
test.describe.serial é a exceção deliberada: roda os testes do grupo em
ordem, na mesma worker, e permite compartilhar uma variável entre eles:
test.describe.serial("ciclo de vida de um produto via API", () => {
let produtoId;
test("POST com token cria produto", async ({ request }) => {
const res = await request.post("/api/produtos", {
headers: AUTORIZADO,
data: { nome: "Produto de teste", preco: 50, categoria: "Acessórios", emoji: "🧪" },
});
const corpo = await res.json();
produtoId = corpo.item.id;
});
test("PUT atualiza o preco", async ({ request }) => {
const res = await request.put(`/api/produtos?id=${produtoId}`, {
headers: AUTORIZADO,
data: { preco: 120 },
});
expect(res.status()).toBe(200);
});
test("DELETE remove o produto", async ({ request }) => {
const res = await request.delete(`/api/produtos?id=${produtoId}`, {
headers: AUTORIZADO,
});
expect(res.status()).toBe(200);
});
});
await res.json() lê o corpo da resposta já
convertido de JSON pra objeto JavaScript, é assim que o teste pega o
id gerado pra usar no próximo passo. Use
test.describe.serial com moderação: fora desse tipo de cenário
(dependência real entre passos), testes independentes continuam sendo a regra, porque
rodam em paralelo e uma falha não arrasta as outras.
4. Sem banco, sem reset: o cuidado que isso exige
O QA Treino guarda os produtos em memória, sem banco de verdade. Isso significa duas coisas importantes pros seus testes:
- os produtos criados em teste ficam lá até o servidor reiniciar, não existe endpoint de reset;
- por isso, todo teste que cria um produto neste módulo também o
apaga no fim do próprio teste (ou do grupo, no caso do
describe.serialacima) — é o padrão de limpar o que você sujou, e vale pra testes de API tanto quanto pra testes de UI; - os 12 produtos originais (ids 1 a 12) nunca devem ser editados ou apagados nos seus testes, porque os Módulos 1 a 4 (e a prova deles) dependem desses dados continuarem do jeito que estavam.
5. Os testes deste módulo
Arquivo já existe em
tests/05_api.spec.js. Não precisa digitar, só rodar:
npx playwright test tests/05_api.spec.js
Exercício
Em tests/05_api.spec.js, escreva um teste chamado
"PUT sem token retorna 401 e nao muda o preco": faça um
request.put("/api/produtos?id=1", { data: { preco: 999 } })
sem o header Authorization, confira que a resposta é
401, e depois confira com um GET que o preço do produto 1
continua 249.9 (não mudou). Rode
npx playwright test tests/05_api.spec.js e depois
python verificar.py 5 até dar 8 [ok]. Gabarito em
trilha-7/GABARITO.md.
📁 Ver estrutura de pastas da trilha-7/
trilha-7/
├── playwright.config.js
├── package.json
├── verificar.py
├── GABARITO.md
├── README.md
├── fixtures/
│ └── exemplo.png
├── pages/
│ ├── LoginPage.js
│ ├── CadastroPage.js
│ ├── ProdutosPage.js
│ ├── ProdutoPage.js
│ └── RedePage.js
└── tests/
├── 01_primeiro_teste.spec.js
├── 02_selecionadores.spec.js
├── 03_cenarios.spec.js
├── 04_rede_ci.spec.js
├── 05_api.spec.js ← este exercício
└── 06_desafio_final.spec.js
Olhar de QA
Testar API é geralmente mais barato e mais estável do que testar a mesma coisa pela UI: sem navegador, sem seletor, sem esperar animação. Por isso, quando um cenário pode ser verificado nas duas camadas, a camada de API costuma ser onde cobrir mais casos de borda (status, validação, autorização) compensa mais, e a UI fica pra confirmar que tudo isso aparece certo na tela pro usuário final. O desafio final deste trilha combina as duas camadas no mesmo teste, que é exatamente esse raciocínio na prática: API pra preparar e mudar o dado, UI pra confirmar o que a pessoa realmente vê.