Entrevistas de Playwright testam julgamento, não vocabulário. Um entrevistador que ouve "getByRole é melhor do que seletores CSS" vai perguntar por quê, e "é mais legível" não é uma resposta que passa. Saber que locators getByRole quebram quando o nome acessível muda, não quando a classe CSS muda, é o tipo de profundidade que separa respostas júnior de sênior.

Conceitos básicos

O que é o Playwright e como ele difere do Selenium?

O Playwright é um framework de automação de testes para aplicações web desenvolvido pela Microsoft. As principais diferenças arquiteturais do Selenium:

Auto-waiting: o Playwright espera os elementos estarem acionáveis antes de interagir, então não precisa de WebDriverWait nem ExpectedConditions. O Selenium exige código de espera explícito.

Tudo integrado: o Playwright vem com test runner, biblioteca de assertions, trace viewer e reporter HTML. O Selenium é uma biblioteca de automação de navegador, então você monta o resto por conta própria.

Protocolo de navegador: o Playwright usa CDP (Chrome DevTools Protocol) para Chromium e protocolos similares para Firefox e WebKit. O Selenium usa o protocolo WebDriver, que é mais lento devido a mais round-trips de rede.

Design JavaScript-first: a API JavaScript/TypeScript do Playwright usa async/await nativo. Os bindings JS do Selenium foram adicionados depois da implementação Java e deixam isso claro.

Como funciona o auto-waiting no Playwright?

Antes de executar uma ação, o Playwright verifica que o elemento alvo atende a um conjunto de condições de "acionabilidade". Para click(): o elemento deve estar visível, no viewport, não coberto por outro elemento, habilitado e estável (não se movendo por causa de animação). Para fill(): o elemento deve estar visível e editável.

Essas verificações rodam automaticamente em um loop de polling até passarem ou o timeout ser atingido. Você pode ver a verificação de acionabilidade na mensagem de erro quando um teste falha. Ela diz exatamente qual condição não foi atendida.

Qual a diferença entre page.locator() e os métodos getBy*? page.locator() aceita um seletor CSS, XPath ou string de texto. É um locator de propósito geral.

Os métodos getBy* (getByRole, getByLabel, getByText, getByTestId, etc.) são locators semânticos. Eles encontram elementos da forma que um usuário os identificaria, usando atributos de acessibilidade em vez de estrutura CSS.

Os métodos getBy* são preferidos porque são mais resilientes a mudanças de UI. Renomear uma classe CSS não os quebra, mas mudar o nome acessível de um botão quebra, que é exatamente o tipo de mudança que afeta usuários reais.

Explique as fixtures do Playwright.

Fixtures são o sistema de injeção de dependência do Playwright. Elas fornecem objetos pré-construídos para os testes sem exigir setup manual em cada teste. As fixtures nativas são: page (uma página de navegador), context (um contexto de navegador), browser (a instância do navegador), request (um cliente HTTP) e browserName.

Fixtures customizadas estendem essas:

const test = base.extend({
  loggedInPage: async ({ page }, use) => {
    await page.goto('https://lab.becomeqa.com');
    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();
    await use(page);  // entrega a página logada para o teste
  }
});

test('dashboard mostra itens', async ({ loggedInPage }) => {
  await expect(loggedInPage.getByText('My Travel Items')).toBeVisible();
});

O que é isolamento de testes no Playwright?

Cada teste do Playwright roda em seu próprio contexto de navegador por padrão: uma lousa em branco sem cookies, sem localStorage, sem dados em cache de outros testes. Isso significa que os testes não interferem entre si mesmo quando rodam em paralelo.

Você pode compartilhar um contexto entre testes dentro de um bloco test.describe usando test.describe.configure({ mode: 'serial' }), mas isso reduz o isolamento e deve ser usado com parcimônia.

Locators e assertions

Quando usar getByTestId em vez de getByRole? getByRole é preferido quando o elemento tem um role ARIA significativo e nome acessível. getByTestId é útil quando:

O elemento não tem um role semântico claro (um componente customizado que renderiza como uma div genérica).

O nome acessível é dinâmico ou não determinístico.

Os desenvolvedores adicionaram explicitamente atributos data-testid como ganchos de teste estáveis.

A regra prática: use getByRole primeiro, recorra a getByTestId quando getByRole não funcionar bem.

O que expect(locator).toBeVisible() verifica de fato?

Verifica que o elemento está presente no DOM, não está oculto por CSS (display: none, visibility: hidden, opacity: 0) e tem dimensões diferentes de zero. Um elemento com display: none falha nessa verificação. Um elemento fora do viewport mas não oculto ainda passa.

toBeVisible() também faz auto-retry. Continua verificando até a assertion passar ou o timeout expirar. Isso é diferente de uma simples verificação booleana. Como fazer assertions em múltiplos elementos de uma vez?

// Verifica count
await expect(page.getByRole('row')).toHaveCount(6);

// Verifica que todos os itens de uma lista contêm texto
const items = page.getByRole('listitem');
await expect(items).toContainText(['Tokyo', 'Paris', 'Oslo']);

// Verifica texto de cada item
await expect(items).toHaveText(['Tokyo', 'Paris', 'Oslo']); // correspondência exata

Qual a diferença entre page.waitForSelector() e usar uma assertion de locator? page.waitForSelector() é uma API de nível mais baixo que aguarda o elemento aparecer no DOM. É anterior à API de locators do Playwright.

Assertions de locator como await expect(locator).toBeVisible() são a abordagem moderna. São mais expressivas, fazem auto-retry e funcionam consistentemente com o sistema de locators do Playwright. Use assertions nos testes, não waitForSelector.

Rede e API

Como interceptar uma requisição de rede no Playwright?

// Intercepta e modifica uma resposta
await page.route('**/api/items', route => {
  route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify([{ id: 1, destination: 'Cidade Mockada' }])
  });
});

// Bloqueia requisições específicas
await page.route('**/*.png', route => route.abort());

// Modifica headers de requisição
await page.route('**/api/**', route => {
  route.continue({
    headers: { ...route.request().headers(), 'X-Custom-Header': 'test' }
  });
});

Qual a diferença entre page.request e a fixture request? page.request compartilha o contexto do navegador. Envia requisições com os mesmos cookies e estado de autenticação da página atual do navegador.

A fixture request é um cliente HTTP independente sem sessão de navegador. Use request para testes de API puros que não precisam do estado do navegador. Use page.request quando precisar fazer chamadas de API com a sessão do usuário atualmente logado.

Como aguardar uma requisição de rede completar antes de prosseguir?

// Aguarda uma requisição e resposta específicas
const responsePromise = page.waitForResponse('**/api/items');
await page.getByRole('button', { name: 'Refresh' }).click();
const response = await responsePromise;
expect(response.status()).toBe(200);

Configuração e CI

Como rodar testes do Playwright no GitHub Actions?

# .github/workflows/playwright.yml
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: playwright-report
    path: playwright-report/

Pontos importantes: npm ci em vez de npm install para reprodutibilidade. npx playwright install --with-deps instala navegadores e suas dependências do sistema. if: always() no upload do relatório garante que você receba o relatório mesmo quando os testes falham.

O que é sharding e quando usá-lo?

Sharding divide a suite de testes entre múltiplas máquinas. Cada shard roda um subconjunto dos testes:

npx playwright test --shard=1/4  # esta máquina roda 25% dos testes
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4

Use quando sua suite leva mais de 5 minutos em uma única máquina e você quer feedback de CI mais rápido. Os testes precisam ser isolados (sem estado compartilhado) para o sharding funcionar corretamente.

O que trace: 'on-first-retry' faz no playwright.config.ts?

Quando um teste falha e é retentado, o Playwright grava um trace. Trata-se de uma gravação completa incluindo snapshots do DOM, requisições de rede, screenshots em cada step e logs de console. O trace é salvo na pasta de resultados e visualizável com npx playwright show-report ou fazendo upload para trace.playwright.dev.

Arquitetura e padrões

Quando usar Page Object Model vs testes diretos?

Use testes diretos quando: você tem poucos testes (menos de 20), os testes são exploratórios, ou está começando agora.

Use POM quando: múltiplos testes interagem com a mesma página, locators são repetidos em arquivos de teste, ou a UI muda frequentemente e você quer atualizar locators em um lugar só.

O gatilho prático: quando você se encontra copiando as mesmas 5+ linhas entre testes, é hora de um page object.

Como lidar com autenticação entre testes sem logar em cada um? storageState salva e restaura cookies e localStorage do navegador. Execute o login uma vez em um arquivo de global setup, salve o estado, e configure os testes para carregá-lo:

// global-setup.ts
import { chromium } from '@playwright/test';

export default async function globalSetup() {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto('https://lab.becomeqa.com');
  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();
  await page.context().storageState({ path: 'auth.json' });
  await browser.close();
}

// playwright.config.ts
export default defineConfig({
  globalSetup: './global-setup.ts',
  use: { storageState: 'auth.json' }
});

Um teste passa localmente mas falha no CI. O que você verifica?

Na ordem: variáveis de ambiente (secrets faltando no CI), URL base (localhost hardcoded), timing (runners de CI são mais lentos, então adicione retries: 1), await faltando, diferenças de versão de navegador. Faça download do artefato de trace do CI e compare a execução passo a passo com o trace local.

PWDEBUG=1 npx playwright test abre o inspector com execução passo a passo. Use para entender o que o Playwright está realmente fazendo antes de assumir que há um bug.

FAQ

Preciso conhecer os internos do Playwright para uma entrevista sênior?

Entender o protocolo CDP, como o auto-waiting é implementado e como as fixtures funcionam internamente demonstra profundidade. Você não vai ser solicitado a implementá-los. Mas explicar "o auto-waiting faz polling de verificações de acionabilidade em um timer com backoff exponencial" é mais impressionante do que "ele espera automaticamente."

Como demonstrar experiência com Playwright se não o usei profissionalmente?

Um repositório no GitHub com 20+ testes cobrindo login, operações CRUD e chamadas de API de um app real é evidência concreta. lab.becomeqa.com foi criado especificamente para isso. Guie o entrevistador pela sua estrutura de testes e explique por que você tomou as decisões arquiteturais que tomou.

→ Veja também: Page Object Model no Playwright: Do Caos à Manutenibilidade | Top 50 Perguntas de Entrevista para QA Engineer em 2026 (Com Respostas) | Assertions no Playwright: O Guia Completo | Fixtures do Playwright Explicadas: Das Integradas às Personalizadas