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

> Vitest vs Jest para CI: velocidade, suporte a ESM, configuração e esforço de migração. Qual test runner é mais rápido e menos doloroso em pipelines.

Source: https://latchkey.dev/pt/learn/tool-comparisons/vitest-vs-jest  
Updated: 2026-08-20

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.

## Comparison

|  | Jest | Vitest |
| --- | --- | --- |
| Suporte a ESM | Desajeitado (transforms) | Nativo |
| Velocidade | Boa | Muitas vezes mais rápido (transform do Vite) |
| Configuração | Madura, muitos presets | Mínima, alinhada ao Vite |
| Ecossistema | O maior | Crescendo, API compatível com Jest |
| Configuração | Reaproveita o `vite.config.ts` | Configuração e cadeia de transform próprias do Jest |
| Asserções | Chai com `expect` compatível com Jest | `expect` |
| Watch mode | Reexecução no estilo HMR | Watch padrão |
| Cobertura | v8 ou Istanbul | babel-plugin-istanbul ou v8 |
| Modo navegador | Sim | Não (apenas simulação jsdom/happy-dom) |
| Benchmark / teste de tipos | Tinybench, expect-type | Não |
| Sharding | Sim | Sim, `--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() }));
```

> If your codebase is CommonJS and staying CommonJS, none of this applies to you and it is not a reason to migrate. This section is the whole argument for teams that have moved to ESM, and irrelevant to teams that have not.

## 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.

| Approach | Type-checks tests? | Cost |
| --- | --- | --- |
| Jest + babel-jest | No | Fast, but type errors in tests go unnoticed |
| Jest + ts-jest | Yes | Noticeably slower; type-checks on every run |
| Vitest | Via your existing tsconfig | No extra transform config |

> The babel-jest route surprises teams: Jest documentation is explicit that Babel only transpiles and does not type-check, so tests can contain type errors that CI never reports. If you are on babel-jest today, check whether that is a decision you made or one you inherited.

## 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.

> O que não é aproveitado: transformers customizados do Jest, cadeias de transform do `jest.config.js` e qualquer coisa que dependa de internals do Jest ou de um plugin específico dele. Faça o inventário disso primeiro, porque é ali que está a parte ilimitada do trabalho.

## 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.

> O Jest não está em declínio; está na v30 e ativamente mantido. "Todo mundo está migrando para o Vitest" não é uma razão por si só, e uma suíte de testes que funciona vale mais que uma moderna.

## 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.

## O veredito

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.

## FAQ

### 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.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
