Pular para o conteúdo
LatchkeyLatchkey home

Vitest vs Jest em CI: velocidade, ESM e migração

O tempo de execução dos testes costuma ser a etapa mais longa do CI - Vitest e Jest diferem bastante em velocidade e ESM.

Ambos rodam testes JS/TS; o Vitest é nativo do Vite e ESM-first, o Jest é o padrão consolidado com um ecossistema enorme.

Vitest vs Jest at a glance

JestVitest
Suporte a ESMDesajeitado (transforms)Nativo
VelocidadeBoaMuitas vezes mais rápido (transform do Vite)
ConfiguraçãoMadura, muitos presetsMínima, alinhada ao Vite
EcossistemaO maiorCrescendo, API compatível com Jest
ConfiguraçãoReaproveita o vite.config.tsConfiguração e cadeia de transform próprias do Jest
AsserçõesChai com expect compatível com Jestexpect
Watch modeReexecução no estilo HMRWatch padrão
Coberturav8 ou Istanbulbabel-plugin-istanbul ou v8
Modo navegadorSimNão (apenas simulação jsdom/happy-dom)
Benchmark / teste de tiposTinybench, expect-typeNão
ShardingSimSim, --shard desde a v28

Em CI

O Vitest tende a ser mais rápido e evita as dores de cabeça de transform de ESM que causam as falhas "Cannot use import statement" do Jest. O Jest continua sendo a escolha segura para suítes grandes e maduras.

  • Static import statements are evaluated before your code runs, so the hoisting jest.mock() depends on cannot happen under ESM.
  • require() of an ESM file with top-level await throws ERR_REQUIRE_ASYNC_MODULE.
  • The jest object has to come from @jest/globals or import.meta.jest rather than being ambient.
  • Jest documentation notes the underlying Node APIs it uses for this are themselves experimental.
Mocking a module under ESM
# Jest, ESM mode
NODE_OPTIONS="$NODE_OPTIONS --experimental-vm-modules" npx jest

# ...plus transform: {} in config (or a transformer emitting ESM),
# ...plus extensionsToTreatAsEsm for .ts/.jsx,
# ...and jest.mock() no longer works:

const { charge } = await import('./payments.js');
jest.unstable_mockModule('./payments.js', () => ({ charge: jest.fn() }));

# Vitest, no flags, no mode
vi.mock('./payments.js', () => ({ charge: vi.fn() }));

Ambos podem estourar a memória

Suítes grandes estouram a memória dos workers de CI em qualquer runner - limite os workers e dimensione a memória de qualquer forma.

ApproachType-checks tests?Cost
Jest + babel-jestNoFast, but type errors in tests go unnoticed
Jest + ts-jestYesNoticeably slower; type-checks on every run
VitestVia your existing tsconfigNo extra transform config

Por que a migração é mais barata do que parece

O Vitest implementa deliberadamente um expect compatível com Jest e utilitários de mock compatíveis com Jest. Isso torna o grosso de uma migração mecânico em vez de uma reescrita.

  • describe, it, test, beforeEach e afins são iguais.
  • Os matchers de expect(...) são compatíveis com Jest, então as asserções em geral não mudam.
  • jest.fn() vira vi.fn(), jest.spyOn() vira vi.spyOn(), jest.mock() vira vi.mock(). Na maior parte, localizar e substituir.
  • Snapshots são compatíveis em formato com Jest, então arquivos de snapshot existentes geralmente são aproveitados.
  • globals: true na configuração do Vitest mantém describe/it/expect ambientes, então você não precisa adicionar imports em cada arquivo de teste no primeiro dia.

Velocidade, com honestidade

O Vitest costuma ser mais rápido, sobretudo em watch mode, onde reexecuta apenas o que mudou por meio de um grafo de dependências no estilo Vite. No CI em uma máquina fria a diferença é menor do que os benchmarks sugerem, porque ambos gastam boa parte do tempo de relógio em instalação e transform, não em asserções.

  • O watch mode é onde a diferença é mais óbvia e onde as pessoas a sentem todo dia.
  • Os dois suportam sharding entre máquinas de CI, então em grande escala convergem para o que o seu paralelismo permitir.
  • Se você usa ts-jest, parte da lentidão medida do Jest é verificação de tipos a cada execução, não o runner. Compare com babel-jest antes de atribuir isso ao Jest em si.

Quando permanecer no Jest

  • Sua base é CommonJS e vai continuar assim. O principal argumento para migrar não se aplica.
  • Você depende de um transformer customizado do Jest ou de um plugin específico dele sem equivalente no Vitest.
  • Você está em React Native, onde o preset do Jest é o caminho trilhado.
  • Sua suíte é grande, estável e ninguém reclama. Migração é um custo real contra um benefício medido em melhorias de experiência de desenvolvimento.

Conduzindo uma migração com segurança

  1. Inventarie transformers customizados, plugins do Jest e qualquer coisa que toque internals do Jest. Esta é a única parte ilimitada.
  2. Adicione o Vitest ao lado do Jest e configure globals: true para que os arquivos de teste existentes não precisem de edição.
  3. Converta um diretório. Rode as duas suítes no CI e compare os resultados, não apenas a contagem de passa/falha.
  4. Faça localizar e substituir de jest. por vi. nos arquivos convertidos, e conserte o punhado que não mapeia direto.
  5. Regenere os snapshots deliberadamente e revise o diff em vez de aceitá-lo em bloco.
  6. Remova o Jest apenas quando o diff estiver vazio e a suíte inteira tiver rodado verde no novo runner por uma sprint completa.

The switching cost is mostly in the parts nobody lists

  • Assertions and mocks usually port mechanically when the target implements a compatible API; custom transformers and framework plugins do not.
  • Snapshot formats differ between runners, so plan to regenerate and review rather than port.
  • Run both suites in parallel in CI for a period and diff the results. A migration that changes which tests fail is not a migration, it is a regression you have not found yet.
  • Coverage numbers move on a runner change even when the tests do not, because instrumentation differs. Re-baseline any coverage gate deliberately.

The verdict

Projeto novo ou com muito ESM: Vitest. Suíte Jest grande já existente: fique no Jest a menos que a dor com ESM force a mudança.

Perguntas frequentes

Devo trocar Jest por Vitest?
Se a sua base é ESM, ou TypeScript sobre um build Vite, sim. O suporte a ESM do Jest ainda é documentado como experimental, exige a flag --experimental-vm-modules do Node e substitui jest.mock() por jest.unstable_mockModule(). Se você está em CommonJS com uma suíte que funciona, não há razão forte para migrar.
O Jest suporta ESM?
Experimentalmente. A documentação do Jest afirma que a implementação pode ter bugs e faltar recursos, e que as APIs do Node das quais depende também são experimentais. É preciso rodar o Node com --experimental-vm-modules, definir transform: {} ou emitir ESM do seu transformer, e usar extensionsToTreatAsEsm para .ts e .jsx.
Por que jest.mock() não funciona com ESM?
O ESM avalia as declarações import estáticas antes de qualquer código seu rodar, então o hoisting do qual jest.mock() depende não pode acontecer. O Jest oferece jest.unstable_mockModule() para ESM, usado com um import() dinâmico do módulo sob teste.
Quão difícil é migrar de Jest para Vitest?
O grosso é mecânico, porque o Vitest implementa um expect compatível com Jest e mocks compatíveis com Jest. jest.fn() vira vi.fn() e assim por diante, snapshots são compatíveis em formato, e globals: true evita editar cada arquivo. A parte ilimitada são transformers e plugins customizados do Jest, então inventarie isso primeiro.
O Vitest é de fato mais rápido que o Jest?
Em geral sim, de forma mais notável em watch mode, onde reexecuta só o que mudou. No CI em máquina fria a diferença encolhe, porque ambos gastam boa parte do tempo de relógio em instalação e transform. Se você usa ts-jest, parte do que você atribui ao Jest é verificação de tipos a cada execução.
O Vitest funciona sem Vite?
Sim. O Vitest roda em um projeto sem build Vite e cria a configuração de que precisa. A vantagem é maior quando você já tem uma configuração Vite, porque então os seus testes e a sua aplicação resolvem módulos de forma idêntica.
O Jest consegue verificar tipos nos meus testes TypeScript?
Só com ts-jest. O caminho babel-jest transpila sem verificar tipos, o que a documentação do Jest afirma explicitamente, então erros de tipo em testes passam em silêncio. Isso pega de surpresa equipes que assumem que a suíte é verificada por tipos quando não é.
O Jest está descontinuado?
Não. O Jest está na versão 30.4 e é ativamente mantido. O Vitest tem forte impulso, especialmente no ecossistema Vite, mas "todo mundo está migrando" não é uma razão técnica, e uma suíte que funciona vale mais que uma da moda.

Guias relacionados