12k
All articles

Testes de Integração vs Testes End-to-End

Testes de integração vs end-to-end para apps web: definições claras, ferramentas JS e quando usar Vitest, MSW, Playwright ou Cypress.

OpenReplay Team
OpenReplay Team
Testes de Integração vs Testes End-to-End

Testes de integração verificam se seus próprios componentes e módulos funcionam em conjunto — são executados no CI em milissegundos e simulam sistemas externos; testes end-to-end conduzem toda a aplicação em execução através da interface real como um usuário, são executados contra um build implantado ou de staging, e não simulam nada. Essa única distinção resolve a maior parte da confusão, mas esconde um segundo problema específico do desenvolvimento frontend: a palavra “integração” significa algo diferente para um desenvolvedor JavaScript do que para um engenheiro de QA backend, e a fronteira entre “integração” e “E2E” no navegador é genuinamente nebulosa.

Este artigo traça essa linha em termos de web stack. Ele fornece definições precisas com exemplos concretos, uma comparação entre os critérios que realmente decidem qual escrever, onde cada um se posiciona em relação aos testes unitários, as ferramentas JavaScript que se mapeiam para cada tipo (Vitest, Jest, Testing Library, MSW, Playwright, Cypress), e uma recomendação decisiva sobre como dividir seu orçamento de testes — não a resposta evasiva de “use ambos”.

Principais Conclusões

  • Testes de integração renderizam seus próprios módulos em conjunto com a rede simulada e são executados no CI em milissegundos; testes end-to-end conduzem a aplicação implantada em um navegador real e não simulam nada.
  • Em uma aplicação JavaScript, “integração” geralmente significa Vitest ou Jest com Testing Library e MSW; “end-to-end” geralmente significa Playwright ou Cypress contra sua URL real.
  • A fronteira entre integração e E2E no frontend é um espectro, não uma barreira — onde você a traça é uma decisão de orçamento, não uma lei.
  • Escreva principalmente testes de integração para obter a melhor relação confiança-custo, mantenha um conjunto pequeno de três a cinco testes E2E para suas jornadas de maior valor, e não tente cobrir tudo com E2E.
  • Um bug em produção é, por definição, uma jornada que seus testes nunca validaram, o que torna a reprodução de sessão de uma falha real o caminho mais rápido para o teste ausente.

Testes de integração vs testes end-to-end em resumo

Os dois tipos de teste diferem em todos os eixos que importam para o orçamento: quanto cobrem, quão rápido são executados, onde são executados, o que simulam, com que frequência falham pelo motivo errado, e quais bugs somente eles conseguem detectar.

CritérioTeste de integraçãoTeste end-to-end (E2E)
EscopoVários de seus próprios módulos em conjunto (ex.: um componente + sua camada de dados)Toda a aplicação em execução, do frontend ao backend
VelocidadeMilissegundos a poucos segundosSegundos a minutos por teste
Onde é executadoLocalmente e no CI, em Node ou jsdomContra um build implantado ou de staging, em um navegador real
O que simulaSistemas externos — a rede, APIs de terceirosNada (ou o mínimo possível)
InstabilidadeBaixa — sem rede real, sem temporização real do navegadorMaior — depende de todo o sistema permanecer estável
Custo / manutençãoMenor para escrever e manter funcionandoMaior; suites se deterioram conforme a aplicação muda
Detecta exclusivamenteContratos de API quebrados, respostas malformadas, fiação de estadoAuth, redirecionamentos, configuração de ambiente, scripts de terceiros, estado entre páginas

O que é um teste de integração, com um exemplo web

Um teste de integração renderiza uma árvore de seus próprios componentes em conjunto e valida seu comportamento combinado com a rede simulada, verificando se as unidades que você já testou unitariamente realmente cooperam. Ele não inicializa um navegador e não acessa um servidor real.

Um exemplo concreto: um componente LoginForm conectado a um hook de autenticação e a uma busca de perfil. O teste de integração renderiza o formulário, preenche os campos, envia, e valida que o estado de boas-vindas aparece — mas as chamadas POST /api/login e GET /api/profile são interceptadas e respondidas por um mock. Você está testando a conexão entre o formulário, o hook, a camada de requisição e o resultado renderizado. Você não está testando seu backend real, seu provedor de autenticação, ou seu CDN.

Esta é a camada onde bugs de contrato surgem: o componente espera user.name mas o handler retorna user.fullName, um caminho 401 nunca re-renderiza o banner de erro, ou um indicador de carregamento nunca é limpo. Testes de integração detectam os bugs que vivem entre seus módulos — contratos de API quebrados, respostas malformadas, fiação de estado — sem o custo de um deploy real.

O que é um teste end-to-end, com um exemplo web

Um teste end-to-end conduz sua aplicação implantada real em um navegador real, exatamente como um usuário faria, e valida o que o usuário vê — sem nada simulado. Ele exercita o servidor real, o banco de dados real, o fluxo de autenticação real, redirecionamentos reais, e quaisquer scripts de terceiros que a página carrega.

O exemplo canônico é um usuário fazendo login e realizando um checkout: navegar até a URL em produção, digitar credenciais, enviar, chegar ao dashboard, adicionar um item ao carrinho, concluir o pagamento e confirmar o pedido. Cada camada participa — bundle frontend, rede, API, banco de dados, cookies de sessão, integração de pagamento.

Testes E2E detectam os bugs que só existem em um deploy real: autenticação e redirecionamentos, configuração de ambiente, scripts de terceiros, violações de content-security-policy, e estado entre páginas que sobrevive a uma navegação. Nenhum desses se reproduz quando a rede é simulada, que é exatamente por que o E2E vale seu custo mais elevado para as jornadas que geram receita.

Onde se posicionam: a pirâmide e o troféu

Ambos os tipos de teste se posicionam acima dos testes unitários, que verificam uma única função ou componente de forma isolada. A clássica pirâmide de testes prescreve muitos testes unitários na base, menos testes de integração no meio, e pouquíssimos testes E2E lentos no topo. É um padrão sólido para manter uma suite rápida.

O contraponto moderno é o troféu de testes, popularizado por Kent C. Dodds, que engorda a camada de integração porque testes de integração oferecem a melhor relação confiança-custo para aplicações web — próximos ao uso real, mas sem a instabilidade e o tempo de execução do E2E completo. O raciocínio se baseia no princípio orientador do Testing Library de que quanto mais seus testes se assemelham à forma como seu software é usado, mais confiança eles podem proporcionar. Um teste de integração que renderiza uma árvore de componentes real e valida comportamentos visíveis ao usuário se assemelha muito mais ao uso real do que um teste unitário de um reducer isolado — e o faz em milissegundos. Trate o troféu como uma visão moderna amplamente adotada para aplicações web, não como um substituto universal da pirâmide.

A fronteira nebulosa do frontend

No desenvolvimento frontend, a fronteira entre integração e E2E é um espectro, não uma barreira: um teste com Testing Library que renderiza uma subárvore de componentes é “integração”, um teste com Playwright que carrega sua URL real é “E2E”, e onde você traça a linha é uma decisão de orçamento, não uma lei. Essa fronteira nebulosa é a maior fonte de confusão, porque a mesma palavra significa coisas diferentes em contextos diferentes.

A literatura de QA backend divide os testes de integração em estratégias bottom-up, top-down e big-bang — essa taxonomia é real, mas é jargão de integração de servidor, e raramente ajuda alguém trabalhando com Vitest e Playwright. Ignore-a. O que importa para um desenvolvedor web é o mapeamento prático: “integração” quase sempre significa renderizar uma árvore de componentes com a rede simulada, e “E2E” quase sempre significa conduzir a aplicação implantada em um navegador real. A zona cinzenta no meio — por exemplo, um teste Playwright apontado para um servidor de desenvolvimento local com MSW interceptando a rede — é válida; apenas decida deliberadamente de qual lado do orçamento ela vem.

A toolchain JavaScript para cada tipo, com código

Em uma aplicação JavaScript, um “teste de integração” geralmente significa renderizar uma árvore de componentes e validar seu comportamento com a rede simulada — tipicamente Vitest ou Jest com Testing Library e MSW — enquanto um “teste end-to-end” significa conduzir a aplicação implantada em um navegador real com Playwright ou Cypress.

O Mock Service Worker (MSW) é a peça-chave que torna os testes de integração realistas sem um backend real. Ele intercepta requisições na camada de rede em ambos os ambientes — via Service Worker API no navegador (setupWorker), e via interceptação de requisições de baixo nível no Node (setupServer, que seus testes Vitest ou Jest utilizam e que não precisa de arquivo de service worker). Como a interceptação acontece na fronteira em vez de sobrescrever o fetch, os mesmos handlers alimentam seus testes de integração, seu servidor de desenvolvimento e até suas execuções com Playwright; a documentação diz diretamente: “Imagine usar os mesmos mocks de API durante o desenvolvimento, testes de integração e end-to-end, e também no seu Storybook.”

Aqui está o mesmo recurso de login-e-busca-de-perfil como um teste de integração. Note a API http + HttpResponse do MSW v2, que substituiu o padrão res/ctx da v1:

// login.integration.test.tsx — Vitest 4 + @testing-library/react 16 + MSW 2
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { LoginForm } from './LoginForm'

const server = setupServer(
  http.post('/api/login', () =>
    HttpResponse.json({ token: 'abc' })
  ),
  http.get('/api/profile', () =>
    HttpResponse.json({ name: 'Ada' })
  )
)

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

test('logs in and shows the profile name', async () => {
  render(<LoginForm />)
  await userEvent.type(screen.getByLabelText(/email/i), 'ada@example.com')
  await userEvent.type(screen.getByLabelText(/password/i), 'hunter2')
  await userEvent.click(screen.getByRole('button', { name: /sign in/i }))
  expect(await screen.findByText(/welcome, ada/i)).toBeInTheDocument()
})

Nenhum navegador é iniciado, nenhum servidor é executado, e a rede é totalmente controlada — portanto, este teste é rápido e determinístico. O mesmo recurso como um teste E2E é executado contra a aplicação implantada e não simula nada. O Playwright conduz Chromium, Firefox e WebKit com uma única API, com auto-waiting e asserções orientadas ao web:

// login.e2e.spec.ts — Playwright 1.6x
import { test, expect } from '@playwright/test'

test('user logs in and sees their profile', async ({ page }) => {
  await page.goto('/login')
  await page.getByLabel('Email').fill('ada@example.com')
  await page.getByLabel('Password').fill('hunter2')
  await page.getByRole('button', { name: 'Sign in' }).click()
  await expect(
    page.getByRole('heading', { name: /welcome, ada/i })
  ).toBeVisible()
})

As asserções parecem semelhantes; o que difere é tudo que está por baixo. A spec do Playwright prova que o endpoint de autenticação real, o redirecionamento e o cookie de sessão funcionam em um navegador real — e paga por essa prova em tempo de execução e instabilidade. Um limite prático importante: como os React Server Components assíncronos são recentes, o Jest atualmente não os suporta e o Next.js recomenda testes E2E para componentes assíncronos — um caso concreto onde a camada de integração ainda não alcança e o E2E justifica seu lugar.

Uma regra de decisão por cenário

Escolha o tipo de teste com base no bug que você está tentando prevenir, não por hábito. A regra:

  • Nova interação entre módulos ou um contrato frontend-backend → teste de integração. Conectar um componente a um novo endpoint, um novo reducer a um novo selector, um formulário a uma camada de validação: renderize a árvore, simule a rede com MSW, valide o comportamento.
  • Uma jornada crítica do usuário em toda a stack → um teste E2E. Login, cadastro, checkout, o único fluxo que, se quebrado, custa dinheiro ou confiança.
  • Todo o resto → apoie-se na integração, e não cubra com E2E.

A recomendação prática para uma equipe web: escreva principalmente testes de integração para a melhor relação confiança-custo, mantenha um conjunto pequeno de três a cinco testes end-to-end nas suas jornadas de maior valor, e não tente cobrir tudo com E2E. Um teste E2E para um tooltip é um passivo de manutenção; um teste de integração para ele é um seguro barato.

Por que suites E2E se deterioram, e como mantê-las estáveis

Suites end-to-end se deterioram porque cada teste depende de todo o sistema permanecer estável; a solução é mantê-las pequenas, validar resultados visíveis ao usuário em vez de detalhes de implementação, e deletar os testes que não protegem mais a receita. Um ID de teste renomeado, um deploy de staging mais lento, um widget de terceiro instável, ou um redirecionamento alterado podem tornar uma suite aprovada em vermelha sem nenhuma regressão real — e uma suite que dá falsos alarmes é ignorada. Concretamente:

  • prefira seletores baseados em roles e labels em vez de caminhos CSS frágeis;
  • confie no auto-waiting do Playwright em vez de pausas fixas;
  • isole os dados de teste; e
  • limite a quantidade para que a suite permaneça rápida o suficiente para ser confiável.

Nenhuma suite cobre as jornadas que você não imaginou, porém — e um bug em produção é, por definição, um caminho que seus testes de integração e E2E nunca validaram. A reprodução de sessão justifica seu valor precisamente aqui: uma reprodução de uma falha real mostra a jornada não testada exata que quebrou — o estado do DOM, as chamadas de rede e os erros de console no momento da falha. Reproduções de sessão de fluxos de login e checkout frequentemente revelam modos de falha que nenhum teste local exercitou, como um redirecionamento específico de ambiente ou um script de terceiro bloqueado por CSP. A abordagem correta é transformar essa reprodução no teste ausente: uma quebra no nível de jornada se torna uma nova spec do Playwright, uma quebra de componente ou contrato se torna um novo teste de integração com Testing Library + MSW, e o mesmo bug não pode regredir.

Conclusão

A divisão que se sustenta em produção é principalmente integração, alguns E2E de alto valor, e testes unitários por baixo para lógica pura — integração porque oferece a maior confiança por segundo de execução, E2E porque alguns bugs só existem em um navegador implantado real. Comece escrevendo testes de integração com Vitest ou Jest, Testing Library e MSW para as fronteiras de módulos do seu próximo recurso, reserve o Playwright para o punhado de jornadas que você não pode se dar ao luxo de quebrar, e deixe as falhas reais em produção dizerem qual teste você esqueceu de escrever.

Perguntas Frequentes

Uma única ferramenta como Playwright ou Cypress pode escrever tanto testes de integração quanto end-to-end?

Sim, mas a distinção vem de como você delimita o escopo do teste, não da ferramenta. Playwright e Cypress podem montar um componente de forma isolada com a rede simulada, o que é efetivamente um teste de integração, ou carregar sua URL implantada e exercitar toda a stack, o que é end-to-end. Modos de teste de componentes existem em ambos, enquanto Vitest ou Jest com Testing Library continua sendo o caminho de integração mais comum no ecossistema JavaScript.

Preciso do arquivo mockServiceWorker.js para o MSW em testes de integração com Vitest ou Jest?

Não. O arquivo mockServiceWorker.js só é necessário para o caminho do navegador usando setupWorker, que depende da Service Worker API. Testes de integração baseados em Node são executados através do setupServer, que intercepta requisições usando extensão de classe de baixo nível em vez de um service worker, portanto nenhum arquivo gerado é necessário. Você só gera o script do worker ao simular requisições em um navegador real, como durante o desenvolvimento local ou execuções de teste baseadas em navegador.

Quando devo escrever um teste end-to-end em vez de um teste de integração?

Escreva um teste end-to-end quando o bug que você está prevenindo só existe em um deploy real — autenticação, redirecionamentos, configuração de ambiente, cookies de sessão, scripts de terceiros, violações de content-security-policy, ou estado entre páginas. Esses nunca se reproduzem quando a rede é simulada. Para tudo que vive entre seus próprios módulos, como contratos de API quebrados, respostas malformadas ou fiação de estado, um teste de integração é mais rápido, mais barato e menos instável. Reserve testes end-to-end para suas jornadas de maior valor e receita.

Por que meus testes end-to-end passam localmente mas falham no CI?

Suites end-to-end dependem de todo o sistema permanecer estável, então diferenças entre ambientes locais e de CI as quebram sem nenhuma regressão real. Causas comuns incluem pausas fixas em vez de auto-waiting, seletores CSS frágeis que mudam entre builds, temporização mais lenta de deploy no CI, dados de teste compartilhados ou não isolados, e widgets de terceiros instáveis. Prefira seletores baseados em roles e labels, confie no auto-waiting do Playwright, isole os dados de teste por execução, e mantenha a suite pequena o suficiente para permanecer confiável.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.