Vitest vs Jest: por que migrar os testes do seu projeto JavaScript
1. O cenário atual: Jest como padrão e seus limites
O Jest, criado pelo Facebook em 2014, tornou-se o framework de testes mais popular do ecossistema JavaScript. Sua adoção massiva se deve à experiência integrada — zero configuração para projetos React, asserções embutidas, mocking simplificado e cobertura de código nativa. Milhares de projetos, desde startups até gigantes como Airbnb e Uber, construíram suas suítes de teste sobre ele.
No entanto, à medida que os projetos crescem, as limitações do Jest se tornam evidentes:
- Cold start lento: Em projetos com centenas ou milhares de arquivos, a primeira execução pode levar dezenas de segundos. O Jest precisa processar todo o grafo de dependências antes de começar.
- Watch mode ineficiente: O modo de observação do Jest reinicia o processo inteiro a cada alteração, desperdiçando tempo de CPU.
- Configuração complexa para ESM: Projetos que usam módulos ES modernos exigem plugins adicionais (
babel-jest,ts-jest,@jest/globals), aumentando a complexidade dojest.config.js. - Isolamento de testes pesado: Cada arquivo de teste roda em um worker separado, mas a comunicação com o processo pai é cara.
2. Vitest: o novo concorrente que nasceu moderno
O Vitest surgiu em 2021 como parte do ecossistema Vite, criado por Anthony Fu e Patak. Sua proposta é simples: oferecer a mesma API do Jest, mas com a velocidade e modernidade do Vite.
Principais características:
- Compatibilidade nativa com ESM: O Vitest entende módulos ES sem transformação adicional. Seu projeto usa
import/export? Funciona direto. - API idêntica ao Jest:
describe,it,expect,jest.fn()→vi.fn(). A migração é quase drop-in. - TypeScript e JSX sem plugins: O Vite já lida com TypeScript e JSX nativamente. Não precisa de
ts-jestoubabel. - CSS Modules: O Vitest processa imports de CSS como objetos vazios ou mocks, sem configuração extra.
3. Comparação prática de performance e velocidade
Vamos comparar um projeto real com 500 arquivos de teste:
Cold start
Jest:
$ jest
PASS src/__tests__/api.test.ts (12.3s)
PASS src/__tests__/utils.test.ts (8.7s)
...
Test Suites: 500 passed, 500 total
Time: 47.2s
Vitest:
$ vitest run
PASS src/__tests__/api.test.ts (1.8s)
PASS src/__tests__/utils.test.ts (1.2s)
...
Test Suites: 500 passed, 500 total
Time: 12.4s
O Vitest é ~3-4x mais rápido no cold start porque aproveita o cache de módulos do Vite e o sistema de transformação sob demanda.
Watch mode com HMR
Quando você altera um arquivo:
- Jest: Reinicia todo o processo de teste para o arquivo modificado. Em projetos grandes, pode levar 5-10s para ver o resultado.
- Vitest: Usa Hot Module Replacement (HMR). Apenas o módulo alterado é recompilado e os testes relevantes são reexecutados. O feedback é quase instantâneo (< 1s).
Execução paralela
O Vitest usa workers baseados no tinypool (fork do jest-worker), mas com melhor gerenciamento de recursos. Em máquinas com múltiplos núcleos, a paralelização é mais eficiente.
4. Compatibilidade e migração gradual
A migração pode ser feita incrementalmente. Veja o mapeamento básico:
Funções globais
| Jest | Vitest |
|---|---|
jest.fn() |
vi.fn() |
jest.mock() |
vi.mock() |
jest.spyOn() |
vi.spyOn() |
jest.useFakeTimers() |
vi.useFakeTimers() |
jest.clearAllMocks() |
vi.clearAllMocks() |
Configuração
jest.config.js (antes):
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};
vitest.config.ts (depois):
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
alias: {
'@': '/src',
},
},
});
Perceba: sem presets, sem transformadores extras. O Vitest entende TypeScript nativamente.
Migração gradual
- Instale
viteste configure ovitest.config.ts - Adicione
"test": "vitest run"aopackage.json - Execute
npx vitest run— ele tentará rodar todos os testes - Ajuste importações: substitua
jestporvinos arquivos de teste - Corrija diferenças de comportamento (veja seção 6)
5. Funcionalidades que o Jest não entrega (ou entrega mal)
Simulação de ambiente de navegador
O @vitest/browser permite testar componentes React/Vue com um navegador real (Playwright) ou headless, sem precisar de jsdom ou happy-dom:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
},
});
Mocking de módulos ESM
O Jest tem problemas históricos com mocking de módulos ESM. O Vitest resolve isso nativamente:
// __mocks__/api.ts
export const fetchData = vi.fn(() => Promise.resolve({ data: 'mock' }));
// test.ts
vi.mock('@/services/api');
Cobertura de código integrada
O Vitest usa c8 (baseado no V8) ou Istanbul para cobertura, com configuração mínima:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
include: ['src/**/*.ts'],
},
},
});
vi.hoisted
Uma funcionalidade exclusiva: permite executar código no topo do escopo do módulo, antes de qualquer outra coisa:
import { vi } from 'vitest';
const mockedDate = vi.hoisted(() => new Date('2023-01-01'));
vi.useFakeTimers();
vi.setSystemTime(mockedDate);
6. Casos de borda e armadilhas na migração
Hoisting de vi.mock()
No Jest, jest.mock() é automaticamente hoisted para o topo do arquivo. No Vitest, vi.mock() também é hoisted, mas com diferenças sutis:
// Jest: funciona
jest.mock('fs');
const fs = require('fs');
// Vitest: funciona
vi.mock('fs');
import { readFileSync } from 'fs';
Mas cuidado com variáveis:
// Jest: funciona
const path = '/tmp';
jest.mock('fs', () => ({ readFileSync: () => path }));
// Vitest: NÃO funciona (path não está no escopo)
const path = '/tmp';
vi.mock('fs', () => ({ readFileSync: () => path }));
// Solução: use vi.hoisted
const path = vi.hoisted(() => '/tmp');
vi.mock('fs', () => ({ readFileSync: () => path }));
Timers falsos
// Jest
jest.useFakeTimers();
jest.advanceTimersByTime(1000);
// Vitest
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
Funciona igual, mas o Vitest oferece vi.useFakeTimers({ shouldAdvanceTime: true }) para timers que avançam automaticamente.
Matchers customizados
Se você usa jest-extended, instale @vitest/expect e configure:
import { expect } from 'vitest';
import * as matchers from 'jest-extended';
expect.extend(matchers);
7. Quando NÃO migrar: cenários onde Jest ainda vence
Nem todo projeto deve migrar. Considere manter o Jest se:
- Projetos legados com plugins Jest profundos: Se você usa
jest-jasmine2,jest-circuscustomizado, ou plugins que dependem de APIs internas do Jest, a migração pode ser trabalhosa. - Ambientes corporativos com ferramentas específicas: Algumas plataformas de CI/CD têm integração profunda com Jest (relatórios JUnit, dashboards).
- Equipes sem familiaridade com Vite: Se seu time não conhece o ecossistema Vite, o custo de aprendizado pode superar os ganhos de performance.
- Projetos pequenos (< 50 testes): Para projetos pequenos, a diferença de performance é irrelevante. A estabilidade do Jest pode ser mais importante.
8. Decisão final: checklist para avaliar sua migração
Use este roteiro de decisão:
Pergunta 1: Seu projeto usa ESM nativamente?
Sim → Vá para pergunta 2
Não → Considere migrar para ESM primeiro ou mantenha Jest
Pergunta 2: O cold start dos testes leva mais de 30 segundos?
Sim → Vitest é recomendado
Não → O ganho pode ser pequeno
Pergunta 3: Você depende de plugins Jest não suportados pelo Vitest?
Sim → Verifique a lista de compatibilidade (jest-extended, jest-chain funcionam)
Não → Migração é segura
Pergunta 4: Sua equipe conhece Vite?
Sim → Migre agora
Não → Invista 1-2 dias em treinamento
Pergunta 5: Você precisa de testes de navegador?
Sim → Vitest com @vitest/browser é superior
Não → Ambos atendem
Resultado:
4-5 "Sim" → Migre imediatamente
2-3 "Sim" → Migre gradualmente (um pacote por vez)
0-1 "Sim" → Mantenha Jest por enquanto
Plano de rollout sugerido
- Semana 1: Instale Vitest e configure o
vitest.config.ts. Execute os testes em paralelo com Jest. - Semana 2: Migre os primeiros 20% dos testes (módulos mais simples).
- Semana 3: Migre os 80% restantes. Ajuste casos de borda.
- Semana 4: Remova Jest do
package.jsone comemore a velocidade.
Conclusão
O Vitest não é apenas "Jest, mas mais rápido". É uma reimaginação moderna de como testes deveriam funcionar: nativo para ESM, TypeScript e ecossistema atual. Para projetos novos, a escolha é clara. Para projetos existentes, a migração vale o esforço quando a performance é um gargalo.
A decisão final depende do seu contexto, mas o ecossistema está se movendo rapidamente. O Jest não desaparecerá, mas o Vitest já conquistou seu lugar como a ferramenta de teste padrão para projetos modernos.
Referências
- Documentação oficial do Vitest — Guia completo de instalação, configuração e migração do Jest para Vitest
- Comparação Vitest vs Jest (Vitest docs) — Tabela detalhada de diferenças de API e comportamento entre os frameworks
- Migrando do Jest para o Vitest (Blog do Anthony Fu) — Artigo do criador do Vitest explicando motivações e detalhes técnicos da migração
- Vitest: The Next Generation of Testing (LogRocket) — Tutorial prático mostrando configuração e exemplos de uso em projetos React
- Jest vs Vitest: Performance Benchmark (Medium) — Benchmark real com métricas de tempo de execução em projetos de diferentes tamanhos
- Documentação do Vite — Base do ecossistema onde o Vitest se apoia, com explicações sobre HMR e transformação de módulos
- Vitest Browser Mode (Playwright) — Guia oficial para testes de componentes em ambiente de navegador real com Playwright