Contexto
Praticamente todo projeto TypeScript hoje já tem um bundler configurado antes de escrever o primeiro teste — e é exatamente essa pipeline de transformação existente, ou a falta dela, que separa Vitest de Jest. Jest é o runner mais estabelecido do ecossistema, com pipeline de transformação própria (historicamente Babel, hoje também com suporte a ts-jest ou ao transform nativo do SWC via plugins) e uma API de teste que virou referência — describe, it, expect — replicada por praticamente todo runner que veio depois. Vitest nasceu dentro do ecossistema Vite com uma proposta direta: em vez de reimplementar uma pipeline de transformação própria, reaproveita a mesma pipeline que já transforma o código da aplicação em desenvolvimento (esbuild, por padrão, dentro do Vite).
Essa decisão arquitetural — reaproveitar vs implementar a própria pipeline — é a raiz de quase todas as diferenças práticas entre os dois.
Quando escolher Vitest
Vitest faz mais sentido quando o projeto já usa Vite como bundler: o mesmo vite.config.ts (ou plugins equivalentes) que transforma TypeScript, JSX e imports de assets no dev server é reaproveitado para rodar os testes, sem manter duas configurações de transformação em paralelo.
import { describe, it, expect, vi } from "vitest";
describe("soma", () => {
it("soma dois números", () => {
const spy = vi.fn((a: number, b: number) => a + b);
expect(spy(2, 3)).toBe(5);
});
});
A API acima é deliberadamente parecida com a do Jest — describe/it/expect funcionam quase de forma intercambiável, e vi.fn/vi.mock espelham jest.fn/jest.mock — o que reduz a fricção de quem já testou com Jest antes. O ganho prático de Vitest aparece em projetos ESM-first ou que usam recursos modernos do Vite (import de CSS/assets, path aliases, plugins): não é preciso duplicar essa configuração para o ambiente de teste. O watch mode também tende a ser mais ágil nesses projetos, porque reaproveita o grafo de módulos que o Vite já mantém em memória durante o desenvolvimento. Cobertura de código também sai de graça na mesma pipeline: o provider padrão usa o coverage nativo do V8, sem precisar instrumentar o código com um transform separado antes de medir.
Quando escolher Jest
Jest continua sendo a escolha mais segura quando o projeto não usa Vite — não faz sentido introduzir uma dependência de pipeline do Vite só para rodar testes se o build de produção não passa por ele. Jest também carrega um volume histórico de exemplos, plugins de terceiros e integrações já validadas em produção — resultado de ter sido, por mais tempo, o runner de referência do ecossistema JavaScript antes da ascensão do Vitest.
describe("soma", () => {
it("soma dois números", () => {
const spy = jest.fn((a, b) => a + b);
expect(spy(2, 3)).toBe(5);
});
});
A API é praticamente idêntica à do Vitest no exemplo acima — não por coincidência, já que Vitest foi desenhado para ser compatível — mas o transform por trás é diferente: Jest depende de Babel ou de um preset como ts-jest para converter TypeScript e sintaxe moderna antes de executar, o que é uma segunda pipeline de transformação para manter além da do bundler de produção. Suporte a ESM nativo historicamente exigiu configuração adicional (flags experimentais, presets específicos), enquanto Vitest trata ESM como padrão. Em compensação, monorepos legados com configuração Jest já madura e testada raramente têm um motivo forte para migrar só pela migração.
Veredito
Reduzida ao essencial, a escolha tem uma única variável: qual bundler o projeto já roda. Se o projeto roda em Vite (ou em uma stack ESM-first equivalente), Vitest evita manter duas pipelines de transformação de código e tende a ter watch mode mais ágil, com uma API compatível o suficiente para não exigir reaprendizado. Se o projeto não usa Vite, ou já tem uma configuração Jest madura com integrações específicas validadas, a migração raramente compensa o esforço — Jest carrega mais tempo de estrada nesse ecossistema, o que se traduz em mais integrações de terceiros já validadas.
Migrar um projeto Jest maduro para Vitest só pela promessa de velocidade raramente compensa o esforço de reescrever configuração e ajustar mocks que já funcionam. Já num projeto novo construído sobre Vite, a pergunta quase nem chega a se colocar: manter duas pipelines de transformação rodando em paralelo, uma para o build e outra só para os testes, é o tipo de complexidade acidental que vale evitar desde o primeiro commit.