Alternar tema

Testes de componentes com Vitest: Browser Mode e integração com Playwright

Easton editorial illustration: 组件测试样品, jsdom 模拟舱, Browser 真实渲染舱, CI 覆盖率闸门

Para ser bem sincero, a primeira vez que testei um componente com Canvas eu caí em uma armadilha grande.

Olhei para aquele “PASS” verde no relatório de testes e subi o código cheio de confiança. No dia seguinte, um colega abriu a página em um navegador real: o Canvas simplesmente não renderizava. Mas o teste tinha passado, não tinha?

Depois eu entendi: jsdom é só um navegador “de mentirinha”. Ele simula a DOM API, mas não consegue testar a renderização real de Canvas, os estilos CSS computados nem o ciclo de vida de Web Components. A configuração de testes unitários que eu usava havia meio ano estava testando só a superfície.

É por isso que o Vitest lançou o Browser Mode na versão 3.0: ele roda os testes diretamente em um navegador real. Em vez de simular o DOM no Node.js como o jsdom, o Browser Mode inicia Chromium/Firefox/Safari, renderiza o componente de verdade e usa as APIs do Playwright para interagir com ele. O teste passou? Aí sim passou de verdade.

Neste artigo, você vai configurar o Browser Mode do zero, ver testes práticos de componentes React/Vue e fechar com gates de cobertura no CI. Este é o terceiro texto da série guia de testes com Vitest. Os dois primeiros trataram da configuração de testes unitários e do fluxo de TDD; agora vamos preencher a peça que faltava: testes de componentes.

Por que precisamos do Browser Mode?

Talvez você esteja pensando: jsdom não é suficiente?

Sendo honesto, para componentes de lógica pura, como uma calculadora ou um validador de formulário, jsdom dá conta tranquilamente. Ele é um simulador de DOM no ambiente Node.js, muito rápido e simples de configurar. Os testes unitários dos dois artigos anteriores rodavam todos em jsdom: testar dados reativos, testar disparo de eventos, tudo sem problema.

Mas quando você encontra estes cenários, o jsdom começa a fraquejar:

  • Desenho em Canvas: jsdom tem Canvas API, mas não desenha de verdade. Testar quantas vezes ctx.fillRect() foi chamado, tudo bem; testar se a figura desenhada está correta? Não dá.
  • Estilos CSS computados: getComputedStyle() no jsdom retorna um objeto vazio. Em um navegador real, a largura de um elemento é afetada pelo contêiner pai, por padding e por border; jsdom simplesmente não calcula isso.
  • Web Components: os métodos connectedCallback e disconnectedCallback de elementos personalizados até são simulados no jsdom, mas o momento em que o ciclo de vida dispara não é igual ao de um navegador real.
  • Renderização assíncrona: frames de animação, requestIdleCallback, IntersectionObserver; essas APIs ou não existem no jsdom, ou existem de forma incompleta.

Um exemplo de problema que eu já vivi: no ano passado, em um projeto, havia um componente que usava animação CSS para controlar a expansão e o recolhimento de um botão. Nos testes com jsdom, o evento transitionend nunca disparava, porque não havia uma transição real. O teste virou um evento mockado. Depois, a duração da animação mudou no navegador real, o teste continuou “passando”, mas o componente já estava quebrado.

Browser Mode resolve justamente esse tipo de problema. Ele renderiza o componente em um navegador real: você escreve o teste, ele inicia o Chromium, ou Firefox/Safari, monta o componente na página e usa as APIs do Playwright para clicar, digitar e esperar. O que acontece no navegador real é o que o teste observa.

Há um dado interessante aqui: no Browser Mode do Vitest 3.0, o contexto do Chromium é compartilhado. O navegador abre uma única vez, e todos os testes usam a mesma instância. Segundo a documentação oficial, isso é 30% mais rápido do que o Playwright E2E tradicional. Se você testa 50 componentes, não precisa esperar o navegador abrir e fechar repetidamente.

Outro ponto: a pirâmide de testes fica meio controversa quando falamos de componentes. A pirâmide tradicional coloca testes unitários como a base e E2E como a menor fatia. Mas há uma visão de pirâmide invertida no blog oficial do Vue, citando a pirâmide de testes invertida de alexop.dev: 70% testes de integração, 20% testes unitários e 10% E2E. O motivo é que um componente já é, por si só, uma unidade integrada: ele combina template, estilos e lógica. Com jsdom, o teste unitário costuma cobrir apenas parte da lógica; o teste de integração valida o comportamento como um todo. Browser Mode preenche exatamente esse espaço: mais realista do que jsdom e mais leve do que Playwright E2E.

Configuração prática do Browser Mode

Configurar o Browser Mode não é tão complicado. Ainda assim, há alguns detalhes em que eu já tropecei, então vale antecipar.

Instale as dependências

Primeiro, instale o Vitest e o provedor do Browser Mode. A recomendação oficial é usar Playwright:

npm install -D vitest @vitest/browser-playwright

O Playwright pode instalar Chromium, Firefox e WebKit. Se você quiser testar apenas no Chromium, o que é suficiente na maioria dos casos, pode instalar só ele:

npx playwright install chromium

Essa etapa é um pouco lenta. O pacote do Chromium tem por volta de 170 MB. Depois que o download termina, a base está pronta.

Configuração do vitest.config.ts

O arquivo de configuração é simples, mas há um detalhe importante:

import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'

export default defineConfig({
  test: {
    browser: {
      provider: playwright(),
      enabled: true,
      instances: [{ browser: 'chromium' }],
    },
  },
})

A configuração instances define em qual navegador os testes serão executados. Se você quiser testar compatibilidade em vários navegadores, adicione Firefox e WebKit:

instances: [
  { browser: 'chromium' },
  { browser: 'firefox' },
  { browser: 'webkit' },  // Safari
]

Eu normalmente habilito apenas o Chromium. Testes em vários navegadores são mais lentos, e a maioria dos bugs de frontend já aparece no Chromium. Para problemas de compatibilidade com Safari, costumo cobrir os caminhos críticos com Playwright E2E.

Modo headless vs modo UI

Essa escolha tem suas nuances.

  • Modo headless: o navegador não abre uma janela; os testes rodam em segundo plano. É adequado para CI, é rápido, mas você não enxerga o processo de renderização do componente.
  • Modo UI: o navegador abre uma janela; você vê o componente sendo renderizado, clicado e preenchido. É ótimo para depurar durante o desenvolvimento e ajuda bastante na hora de escrever testes.

Durante o desenvolvimento, uso o modo UI com este comando:

npx vitest --browser.ui

O Vitest abre um painel de testes: à esquerda fica a lista de testes; à direita, a janela do navegador. Ao clicar em um arquivo de teste, o componente é renderizado no navegador, e você consegue acompanhar a execução. Um botão não respondeu ao clique? Dá para depurar diretamente no navegador.

No CI, use o modo headless adicionando uma linha à configuração:

browser: {
  provider: playwright(),
  enabled: true,
  headless: true,  // CI força headless
  instances: [{ browser: 'chromium' }],
}

Convenção de nomes para arquivos de teste

A documentação recomenda usar .browser.test.ts para arquivos de teste em Browser Mode, separando-os dos .test.ts comuns. Testei esse padrão e ele tem duas vantagens:

  • Você consegue rodar testes unitários com jsdom e testes de componentes com Browser Mode ao mesmo tempo, sem misturar responsabilidades.
  • Quando algo falha no CI sem uma causa óbvia, o nome do arquivo já indica que é um teste de navegador, o que deixa a investigação mais direta.

Mas o Vitest não obriga esse nome. Você também pode usar .test.ts. O importante é que a configuração inclua o diretório com testes em Browser Mode, ou que todos os testes rodem diretamente nesse modo. Eu prefiro separar: testes unitários com jsdom, testes de componentes com Browser Mode.

Testes práticos de componentes React/Vue

Esta é a parte central do Browser Mode. Para ser honesto, a escrita difere um pouco do Testing Library tradicional, mas fica bem natural depois que você pega o ritmo.

Testes de componentes React

Primeiro, instale o adaptador para React:

npm install -D @vitest/browser-react

Depois escreva o teste. Imagine um componente Counter, em que clicar no botão incrementa o contador em 1:

// Counter.browser.test.ts
import { page } from '@vitest/browser/context'
import { userEvent } from '@vitest/browser/context'
import Counter from './Counter'

test('contagem ao clicar no botão', async () => {
  // Renderiza o componente no navegador
  await page.mount(<Counter />)

  // Localiza o elemento do botão
  const button = page.getByRole('button', { name: 'Count: 0' })

  // Clica no botão
  await userEvent.click(button)

  // Verifica a mudança do texto
  await expect.element(button).toHaveTextContent('Count: 1')
})

Comparando com Testing Library: Testing Library usa render(), enquanto Browser Mode usa page.mount(). Testing Library usa screen.getByRole(), enquanto Browser Mode usa page.getByRole(). As APIs são parecidas; a diferença é que o objeto page vem do contexto do Vitest Browser Mode.

Um detalhe importante é await expect.element(button). Essa é a Web Testing API do Vitest. Ela espera automaticamente a mudança de estado do elemento, então você não precisa escrever await waitFor() manualmente. O framework cuida dessa espera assíncrona, o que deixa o teste mais limpo do que em muitos exemplos com Testing Library.

Testes de componentes Vue

Em Vue, a escrita é parecida, mas você precisa do adaptador Vue:

npm install -D @vitest/browser-vue

Teste de um componente Vue Counter:

// Counter.browser.test.ts
import { page } from '@vitest/browser/context'
import { userEvent } from '@vitest/browser/context'
import Counter from './Counter.vue'

test('contagem ao clicar no botão', async () => {
  // Renderiza o componente Vue
  await page.mount(Counter)

  // Localiza o botão, clica e valida
  const button = page.getByRole('button', { name: 'Count: 0' })
  await userEvent.click(button)
  await expect.element(button).toHaveTextContent('Count: 1')
})

Para Vue 2, o caminho continua sendo @vue/test-utils; Browser Mode não oferece suporte direto. Em projetos Vue 3, @vitest/browser-vue já resolve.

Um caso real

Em um dos meus projetos, havia um componente de ordenação por arrastar e soltar, implementado com HTML5 Drag & Drop API. No ambiente jsdom, os eventos dragstart e drop não podiam ser simulados de verdade. Era necessário usar mocks. O teste confirmava apenas que “um evento foi disparado”, mas não respondia se a lógica de ordenação estava correta.

Com Browser Mode, o teste fica direto:

test('ordenação por arrastar e soltar', async () => {
  await page.mount(<SortableList items={['A', 'B', 'C']} />)

  const itemA = page.getByText('A')
  const itemC = page.getByText('C')

  // Arrasta A para depois de C
  await userEvent.dragTo(itemA, itemC)

  // Verifica a mudança de ordem
  const items = page.getByRole('listitem')
  await expect.element(items.nth(2)).toHaveTextContent('A')
})

Em um navegador real, a Drag & Drop API dispara de verdade, a lógica de ordenação executa de verdade e a ordem dos elementos realmente muda. O teste passou? Então o comportamento está funcionando de fato.

Esse é o valor do Browser Mode: ele testa comportamento real, não comportamento simulado.

Playwright vs Browser Mode: guia de escolha

Talvez você esteja se perguntando: Browser Mode e Playwright não são ambos testes de navegador? Qual é a diferença?

Em uma frase: Browser Mode testa componentes; Playwright testa fluxos.

Diferenças centrais

CaracterísticaBrowser ModePlaywright
Escopo do testeTeste isolado de um componenteTeste de fluxo com várias páginas
Velocidade de execuçãoCerca de 200 ms por teste2 a 5 s por teste
Custo de inicializaçãoInstância de navegador compartilhadaCada teste inicia de forma independente
Complexidade de configuraçãoBaixa, integrado ao VitestAlta, configuração em projeto separado
Cenário indicadoIteração rápida durante o desenvolvimentoValidação de caminhos críticos antes do release

O Browser Mode se posiciona como teste de componentes: ele monta o componente no navegador, mas testa apenas o comportamento daquele componente. O Playwright se posiciona como E2E: ele abre sua aplicação completa, navega, faz login, envia formulários e testa o fluxo inteiro.

Uma analogia simples: Browser Mode é como um cirurgião observando uma única célula no microscópio; Playwright é como um médico avaliando todo o sistema de órgãos. Cada um tem seu papel, e um não substitui o outro.

Estratégia combinada

Nos meus projetos, costumo usar assim:

  • Browser Mode: testa todos os componentes de UI, como botões, formulários, cards e modais. Escrevo durante o desenvolvimento e rodo junto com os commits. É rápido e dá feedback imediato.
  • Playwright E2E: testa de 3 a 5 caminhos críticos, como ver a página inicial depois do login, consultar resultados depois de uma busca e ver o pedido depois do envio. Roda antes do release ou em builds diários no CI.

Essa combinação tem três vantagens:

  1. Browser Mode cobre a maior parte dos bugs de UI e encontra problemas ainda no desenvolvimento.
  2. Playwright cobre bugs de integração entre páginas e valida antes do release.
  3. O custo de manutenção fica controlado: 50 testes de componentes e 5 testes E2E não arrastam o CI para baixo.

Quando escolher cada um?

Uma regra simples:

  • Escolha Browser Mode quando você quer testar o comportamento de UI de um componente isolado: clique, digitação, renderização e estilos. Se o código do componente mudou, o teste precisa dar feedback rápido.
  • Escolha Playwright quando você quer testar um fluxo com várias páginas: login -> navegação -> ação -> validação. Ou quando precisa testar integração entre sistemas, como frontend + API backend + banco de dados.

Por exemplo: testar um componente de seletor de datas, incluindo limite de intervalo, datas desabilitadas e formato de exibição, pede Browser Mode. Testar o usuário escolhendo uma data na página de reservas, enviando o pedido e indo para a página de pagamento pede Playwright.

Mais um ponto: Browser Mode testa apenas o frontend; Playwright pode testar a stack completa. Se sua aplicação tem uma API backend, Playwright E2E consegue validar a integração entre frontend e backend. Browser Mode testa apenas o componente frontend, então o backend precisa ser mockado.

Gates de cobertura no ambiente CI

A configuração de cobertura é a última etapa no CI. Para ser sincero, eu também não dava tanta atenção a isso, até uma refatoração derrubar a cobertura de 80% para 60% e um bug chegar em produção. Foi aí que o gate de cobertura mostrou seu valor.

Configuração de cobertura

Adicione limites de cobertura em vitest.config.ts:

test: {
  coverage: {
    provider: 'v8',  // ou 'istanbul'
    reporter: ['text', 'json', 'html'],
    thresholds: {
      lines: 80,
      functions: 80,
      branches: 75,
      statements: 80
    }
  }
}

Qual limite faz sentido? Pela minha experiência:

  • Projeto novo: comece com 50% e aumente aos poucos. Definir um limite muito alto no início cria pressão desnecessária na escrita do código.
  • Projeto maduro: 80% é um valor razoável. Módulos centrais podem exigir mais, como 90% ou 95%.
  • Não persiga 100%: ramificações de borda e tratamento de exceções nem sempre compensam o esforço de teste; forçar cobertura total pode desperdiçar tempo.

Rode os testes com cobertura:

npx vitest run --coverage

A cobertura ficou abaixo do limite? O Vitest falha, e o build do CI também falha. Esse é o gate: código abaixo do limite não entra na branch principal.

Integração com GitHub Actions

No workflow de CI, adicione duas etapas: rodar testes e reportar cobertura.

# .github/workflows/test.yml
name: Test

on: [pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci
      - run: npx playwright install chromium --with-deps

      - name: Run tests with coverage
        run: npx vitest run --coverage

      - name: Report coverage
        uses: davelosert/vitest-coverage-report-action@v2
        with:
          json-summary-path: './coverage/coverage-summary.json'

Essa Action comenta automaticamente no PR mostrando a variação de cobertura:

  • percentual de mudança da cobertura, por exemplo de 80% para 79%, exibindo -1%;
  • quais arquivos tiveram queda de cobertura;
  • quais linhas novas ainda não estão cobertas.

Quem abriu o PR vê rapidamente: “Ok, o código novo não está bem coberto, preciso adicionar testes.”

Cuidados no CI

Browser Mode no CI tem alguns pontos de atenção:

  1. Instalação dos navegadores do Playwright: use o parâmetro --with-deps; sem ele, o Chromium pode não iniciar.
  2. Modo headless: force headless: true na configuração, porque o ambiente de CI não tem monitor.
  3. Timeout: testes com Browser Mode são mais lentos do que jsdom; aumente o limite de timeout. Eu uso 30 segundos.
  4. Controle de paralelismo: Browser Mode compartilha a instância do navegador. Não configure paralelismo alto demais; eu limito maxWorkers: 4.

Minha configuração completa de workflow no CI fica assim:

- name: Run browser tests
  run: npx vitest run --coverage --browser.headless
  env:
    CI: true

--browser.headless garante que o navegador não abra uma janela, e a variável CI: true faz o Vitest ajustar automaticamente alguns comportamentos, como desabilitar saída colorida.

Conclusão

Depois de tudo isso, a ideia central é simples: jsdom não testa comportamento real de navegador; Browser Mode testa. Canvas, estilos CSS computados, Web Components, arrastar e soltar, animações: nesses cenários, Browser Mode é a escolha certa.

A configuração não é difícil. Instale @vitest/browser-playwright, ajuste algumas linhas em vitest.config.ts e você já consegue rodar. Em React e Vue, as APIs se parecem com Testing Library, então a curva de aprendizado é baixa.

Mas Browser Mode não é solução para tudo. Ele testa componentes isolados; Playwright testa fluxos. Use os dois em conjunto: durante o desenvolvimento, Browser Mode cobre todos os componentes de UI; antes do release, Playwright E2E valida os caminhos críticos. Assim a cobertura fica ampla e o CI continua rápido.

O gate de cobertura fecha o ciclo. Defina um limite, bloqueie PRs abaixo desse valor e evite que a cobertura caia aos poucos. Com coverage-report-action no GitHub Actions, o PR mostra automaticamente a variação de cobertura, e o problema fica visível de imediato.

Se você ainda não testou Browser Mode, comece por componentes simples: um botão, um campo de entrada, algo pequeno. Configure o Browser Mode, valide que tudo roda corretamente e depois avance para componentes mais complexos. Alguns tropeços vão acontecer, mas depois que a configuração encaixa, escrever testes fica mais fluido e a eficiência no desenvolvimento realmente melhora.

Configurar Vitest Browser Mode para testes de componentes

Configure o Browser Mode do zero, rode testes de componentes React/Vue e integre gates de cobertura no CI

⏱️ Estimated time: 20 min

  1. 1

    Step 1: Instale o provedor Playwright

    Execute npm install -D vitest @vitest/browser-playwright para instalar as dependências e npx playwright install chromium para baixar o navegador.
  2. 2

    Step 2: Configure o vitest.config.ts

    Em test.browser, defina provider: playwright(), enabled: true e instances: [{ browser: 'chromium' }]; no CI, adicione headless: true.
  3. 3

    Step 3: Escreva testes de componentes

    Use page.mount() para renderizar o componente, page.getByRole() para consultar elementos, userEvent.click() para simular interação e expect.element() para validar o resultado.
  4. 4

    Step 4: Configure o gate de cobertura

    Em coverage.thresholds no vitest.config.ts, defina limites como lines: 80; no GitHub Actions, integre vitest-coverage-report-action para mostrar a variação de cobertura no PR.

FAQ

Qual é a diferença entre Browser Mode e jsdom?
jsdom simula o DOM no Node.js, mas não testa renderização em Canvas, estilos CSS computados nem o ciclo de vida de Web Components. Browser Mode renderiza componentes em um navegador real e testa o comportamento real.
Como escolher entre Browser Mode e Playwright E2E?
Browser Mode é indicado para testes de componentes isolados, por volta de 200 ms por teste, com feedback imediato no desenvolvimento. Playwright é melhor para fluxos de várias páginas, em torno de 2 a 5 segundos por teste, validando caminhos críticos antes do release. O ideal é combinar os dois.
Qual limite de cobertura faz sentido?
Em projetos novos, comece com 50%. Em projetos maduros, 80% costuma ser um valor razoável. Módulos centrais podem chegar a 90%. Não persiga 100%, porque alguns cenários de borda não compensam o custo de teste.
Browser Mode oferece suporte ao Vue 2?
Não diretamente. Vue 2 deve usar @vue/test-utils. Em projetos Vue 3, você pode usar @vitest/browser-vue.
Quais cuidados tomar com Browser Mode no CI?
Ao instalar o Playwright, use o parâmetro --with-deps; configure headless: true; aumente o timeout, por exemplo para 30 segundos; e limite o paralelismo, como maxWorkers: 4.

15 min de leitura · Publicado em: 17 mai 2026 · Atualizado em: 14 jul 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog