Neste guia você vai montar um portfólio de automação de testes que mostra como você pensa, e não só que você sabe instalar um framework. Vamos escolher um alvo de prática estável, organizar o repositório, escrever um README que explica estratégia e decisões, separar Page Objects, testes de UI e de API, cuidar dos dados de teste, rodar tudo no GitHub Actions e publicar relatórios. No final há uma lista do que evitar e exemplos curtos em Playwright.
O que um portfólio de automação precisa provar
Quem avalia um repositório de testes raramente lê cada linha. Em poucos minutos, a pessoa quer responder a três perguntas: os testes rodam? O código é fácil de manter? Quem escreveu sabe por que testou o que testou? Na prática, o que diferencia um portfólio forte de um fraco quase nunca é a quantidade de testes, e sim a clareza das escolhas.
Por isso, pense no portfólio como um produto pequeno e bem acabado. Melhor ter dez testes que contam uma história coerente (fluxo feliz, erros esperados, regras de negócio, um bug encontrado) do que cem testes copiados que quebram na primeira execução.
Escolha um alvo de prática estável
O primeiro problema de quem monta portfólio é o alvo. Sites reais mudam sem aviso, bloqueiam robôs ou proíbem automação nos termos de uso. Um alvo feito para prática resolve isso. O AppsQuality Lab foi criado para esse fim: é uma loja fictícia com cinco variantes de front-end da mesma aplicação, cada uma treinando um tipo de seletor:
- HTML: atributos
data-testide ids fixos em todos os elementos, ideal para começar. - React: seletores por texto, papel e rótulo acessível.
- Vue: sem ids nem
data-testid, para exercitar seletores por estrutura e texto. - Legado: navegação com recarga de página e checkout dentro de iframe.
- Shadow DOM: componentes encapsulados, que exigem atravessar a árvore de sombra.
Há ainda uma página de elementos difíceis (abas, tabelas ordenáveis, tooltips, uploads, janelas novas, iframes aninhados) e uma API REST de prática com especificação OpenAPI, base https://appsquality.com.br/wp-json/aq-lab/v1. A senha fictícia é a mesma para todos os usuários, qa@12345, e cada usuário tem um comportamento pensado para teste:
standard_user: tudo funciona como esperado.locked_user: o login sempre responde 423 (conta bloqueada).problem_user: bugs de propósito, como imagens trocadas, ordenação por preço ignorada e produto errado no carrinho.slow_user: login e requisições autenticadas demoram alguns segundos.error_user: o checkout falha com erro 500 em tentativas alternadas.
Esses perfis são ouro para portfólio. Um teste que documenta o bug do problem_user ou que trata a lentidão do slow_user sem sleep fixo mostra maturidade muito mais do que mais um login com sucesso. Os dados da API são temporários e isolados por token, então não envie nada pessoal.
Estrutura de repositório
Uma estrutura previsível ajuda quem avalia a se orientar sem ler o código. Um exemplo enxuto em Playwright com TypeScript:
portfolio-qa/
├── .github/workflows/tests.yml # pipeline de CI
├── pages/ # Page Objects
│ ├── LoginPage.ts
│ └── ProductsPage.ts
├── tests/
│ ├── ui/ # testes de interface
│ │ └── login.spec.ts
│ └── api/ # testes de API
│ └── cart.spec.ts
├── data/ # massa de teste versionada
│ └── users.ts
├── playwright.config.ts
├── package.json
├── .env.example # variáveis sem valores reais
└── README.md
Repare que UI e API ficam separadas, mas no mesmo projeto. Isso permite rodar só um grupo quando for preciso e deixa claro que você enxerga a pirâmide de testes, não apenas a tela.
README: como rodar, estratégia e decisões
O README é a primeira (e às vezes única) coisa lida. Ele deve ter pelo menos três blocos:
- Como rodar: pré-requisitos, instalação e os comandos exatos, de preferência copiáveis. Se a pessoa não consegue rodar em poucos minutos, desiste.
- Estratégia: o que foi coberto e por quê. Por exemplo: “API cobre regras de carrinho e checkout; UI cobre apenas os fluxos críticos de ponta a ponta; o
problem_useré usado para provar que os testes detectam regressões”. - Decisões: escolhas técnicas com justificativa curta, como o motivo de usar
data-testid, por que o login de UI é preparado via API ou por que não há testes visuais ainda.
Uma seção “O que eu faria a seguir” também funciona muito bem: mostra que você conhece os limites do próprio trabalho.
Organização com Page Objects
Page Objects concentram seletores e ações de uma tela em uma classe, e os testes passam a falar a língua do negócio. A documentação do Playwright descreve o padrão como forma de simplificar a escrita e a manutenção, capturando os seletores em um único lugar (playwright.dev/docs/pom, consultado em 13/10/2026). Mantenha as classes enxutas: elas expõem ações e localizadores, e as asserções de regra ficam no teste.
// pages/LoginPage.ts
import { type Page, type Locator } from '@playwright/test';
export class LoginPage {
readonly username: Locator;
readonly password: Locator;
readonly submit: Locator;
readonly error: Locator;
constructor(private readonly page: Page) {
this.username = page.getByTestId('username');
this.password = page.getByTestId('password');
this.submit = page.getByTestId('login-button');
this.error = page.getByTestId('login-error');
}
async goto() {
await this.page.goto('/lab/html/');
}
async login(user: string, pass: string) {
await this.username.fill(user);
await this.password.fill(pass);
await this.submit.click();
}
}
Teste de UI curto em Playwright
Com o Page Object pronto, o teste fica legível até para quem não programa. Aqui, dois cenários na variante HTML do Lab, com baseURL apontando para https://appsquality.com.br no playwright.config.ts:
// tests/ui/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../../pages/LoginPage';
import { users, PASSWORD } from '../../data/users';
test.describe('Login da QA Store', () => {
test('usuária padrão vê o catálogo', async ({ page }) => {
const login = new LoginPage(page);
await login.goto();
await login.login(users.standard, PASSWORD);
await expect(page.getByTestId('products-title')).toHaveText('Produtos');
await expect(page.getByTestId('product-card').first()).toBeVisible();
});
test('usuário bloqueado recebe mensagem clara', async ({ page }) => {
const login = new LoginPage(page);
await login.goto();
await login.login(users.locked, PASSWORD);
await expect(login.error).toContainText('bloqueado');
});
});
Teste de API
A API do Lab devolve um token no login, que vai no cabeçalho Authorization: Bearer ou, se algum proxy remover esse cabeçalho, em X-Lab-Token. O Playwright tem a fixture request para chamadas HTTP sem abrir navegador (playwright.dev/docs/api-testing, consultado em 13/10/2026):
// tests/api/cart.spec.ts
import { test, expect } from '@playwright/test';
import { users, PASSWORD } from '../../data/users';
const API = 'https://appsquality.com.br/wp-json/aq-lab/v1';
test('adiciona produto ao carrinho e calcula subtotal', async ({ request }) => {
const login = await request.post(`${API}/auth/login`, {
data: { username: users.standard, password: PASSWORD },
});
expect(login.status()).toBe(200);
const { token } = await login.json();
const headers = { Authorization: `Bearer ${token}`, 'X-Lab-Token': token };
const add = await request.post(`${API}/cart`, { headers, data: { productId: 1, qty: 2 } });
expect(add.status()).toBe(201);
const cart = await add.json();
expect(cart.items[0]).toMatchObject({ productId: 1, qty: 2 });
expect(cart.subtotal).toBeCloseTo(cart.items[0].price * 2, 2);
});
test('locked_user recebe 423', async ({ request }) => {
const res = await request.post(`${API}/auth/login`, {
data: { username: users.locked, password: PASSWORD },
});
expect(res.status()).toBe(423);
expect((await res.json()).code).toBe('user_locked');
});
Note que o teste não fixa o preço do produto: ele confere a regra (subtotal igual a preço vezes quantidade). Isso deixa o teste resistente a mudanças de catálogo e mostra que você testa comportamento, não números mágicos.
Dados de teste
Concentre usuários, produtos e entradas em um arquivo de dados, e nunca coloque credenciais reais no repositório. Para o Lab, a senha é fictícia e pública, mas o hábito conta: em projetos reais ela viria de variável de ambiente ou de um segredo do CI.
// data/users.ts
export const PASSWORD = process.env.LAB_PASSWORD ?? 'qa@12345';
export const users = {
standard: 'standard_user',
locked: 'locked_user',
problem: 'problem_user',
slow: 'slow_user',
error: 'error_user',
} as const;
Para dados dinâmicos, gere valores únicos por execução (e-mails, nomes, CEPs) e prefira preparar o estado via API. O endpoint POST /reset do Lab restaura os dados do token ao estado inicial, o que ajuda a manter testes independentes. Se precisar de massa variada, o gerador de massa de testes e o gerador de UUID poupam tempo.
CI com GitHub Actions
Um selo verde no README vale mais do que qualquer frase de “testes funcionando”. O guia de CI do Playwright traz um workflow de referência que instala dependências e navegadores, roda os testes e envia o relatório HTML como artefato (playwright.dev/docs/ci-intro, consultado em 13/10/2026). Uma versão adaptada ao portfólio:
# .github/workflows/tests.yml
name: Testes
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Instalar dependências
run: npm ci
- name: Instalar navegadores
run: npx playwright install --with-deps
- name: Rodar testes
run: npx playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
O workflow_dispatch permite disparar a execução manualmente, útil para quem quer ver o pipeline rodar na hora. Como o Lab é público e compartilhado, evite agendar execuções em alta frequência: a API tem limite de requisições por minuto.
Relatórios
Relatório é o que transforma uma execução em evidência. Gere o relatório HTML, guarde-o como artefato e, se quiser ir além, publique-o no GitHub Pages e coloque o link no README. Configure capturas de tela e trace apenas em falha, para o relatório ficar leve e útil na investigação. O próprio guia de CI do Playwright alerta que relatórios e traces podem conter dados sensíveis, como credenciais e tokens; mais um motivo para usar só dados fictícios.
Um bom complemento é uma seção “Bugs encontrados” no README, com o comportamento do problem_user descrito como um relato de defeito: passos, resultado esperado, resultado obtido e o teste que o evidencia.
O que evitar
- Esperas fixas (
sleep,waitForTimeout) para contornar lentidão. Use as esperas automáticas e asserções que aguardam o estado. - Seletores frágeis, como XPath posicional ou classes geradas pelo build.
- Testes dependentes entre si, em que um só passa se o anterior rodou.
- Asserções vazias: teste que clica em tudo e não verifica nada não prova nada.
- Credenciais, tokens ou dados pessoais versionados, inclusive em relatórios publicados.
- Projeto copiado de tutorial sem nenhuma decisão própria explicada.
- README vazio ou com instruções que não funcionam em uma máquina limpa.
- Pipeline vermelho abandonado. Se um teste é instável, corrija ou marque e explique.
Próximos passos
- Abra o AppsQuality Lab, comece pela variante HTML e depois repita o mesmo fluxo nas variantes React, Vue, legado e Shadow DOM.
- Explore a API do Lab e importe a especificação OpenAPI no seu cliente HTTP preferido.
- Use o formatador de JSON e o comparador de JSON para inspecionar respostas da API.
- Quando for automatizar APIs que usam tokens JWT ou valores em Base64, o decodificador de JWT e o conversor Base64 ajudam a entender o que está sendo enviado.
- Gere entradas variadas com o gerador de massa de testes e leia saídas de CI com o formatador de log.
