Pular para o conteúdo
LatchkeyLatchkey home

Vitest vs node:test: Qual Test Runner JS?

O Vitest é um framework de testes rápido movido a Vite com recursos ricos; o node:test é o runner nativo, sem dependências, que vem com o Node.

O Vitest reaproveita sua configuração e transforms do Vite, roda testes em paralelo com watch mode e oferece uma API parecida com a do Jest, mocking, coverage e testes de snapshot - ideal para projetos Vite/TS. O node:test é embutido no Node moderno, não precisa de dependências e cobre as necessidades centrais (test, subtests, asserções via node:assert, mocking básico), mas é mais minimalista. O Vitest é rico em recursos; o node:test é enxuto e sem dependências.

What each one actually supports in 2026

Vitestnode:test
DependênciasVitest + ViteNenhuma (embutido)
RecursosMocks, snapshots, coverageTestes essenciais
TS / ViteDe primeira classeTransform manual
Watch modeHMR rápidoBásico
Melhor paraApps Vite/TSTestes enxutos, sem dependências
Watch modeSim (flag experimental)Sim, reexecução no estilo HMR
TypeScriptRemoção nativa de tiposDireto, via Vite
JSX e testes de componenteNãoVue, React, Svelte e outros
Modo navegadorNãoSim
Reporters de CIspec, tap, dot, junit, lcov embutidosVários, mais sharding para CI distribuído
BenchmarkNãoSim, via Tinybench
Teste de tiposNãoSim, via expect-type
Tags de testeSim, desde o Node 26.2.0Via filtragem

No CI

O Vitest encaixa em projetos já em Vite ou que querem um runner completo com coverage e snapshots; ele paraleliza bem no CI. O node:test é atraente para bibliotecas que não querem dependências de teste e querem inicialização rápida, embora você cuide dos transforms de TS e do coverage (via c8) por conta própria. Escolha Vitest pela profundidade de recursos em apps, node:test pela pegada mínima.

  • --test-reporter=junit and =lcov are built in, so CI test reporting and coverage upload need no extra packages.
  • --test-concurrency=N controls parallel test files; concurrency is not an afterthought.
  • --test-randomize with --test-random-seed makes order-dependence reproducible instead of mysterious.
  • --test-rerun-failures <state-file> reruns only what failed, which is a meaningful CI optimisation.
  • --experimental-test-coverage produces V8 coverage without instrumenting your build.
node:test, current capabilities
import { test, describe, it, mock, snapshot } from 'node:test';
import assert from 'node:assert';

// module mocking, not just function mocking
mock.module('./payments.js', {
  namedExports: { charge: mock.fn(() => ({ ok: true })) },
});

// timer mocking
mock.timers.enable({ apis: ['setTimeout', 'Date'] });

describe('checkout', () => {
  it('charges once', async (t) => {
    const spy = t.mock.method(cart, 'total');
    await checkout(cart);
    assert.strictEqual(spy.mock.callCount(), 1);
  });
});

Acelere

Faça cache das dependências e distribua os testes em shards entre jobs para reduzir o tempo total. Ambos rodam em runners de CI; runners gerenciados mais rápidos encurtam as etapas de execução de testes e transform.

  • Component testing. Vue, React, Svelte and other component tests need a transform pipeline and a DOM. Vitest inherits both from Vite; node:test has neither.
  • Browser mode. Running the suite in a real browser rather than a simulated DOM has no built-in equivalent.
  • JSX and framework transforms. Vitest reuses your existing Vite config, resolvers, and plugins, so tests see the same module graph as your app. Reproducing that under node:test means building it yourself.
  • Benchmarking and type testing. Tinybench-backed benchmarks and expect-type assertions have no built-in counterpart.
  • Sharding. Vitest supports splitting a suite across CI machines natively, which matters once a suite is long enough to need it.
  • Jest compatibility. The Jest-compatible expect and mocking API makes migrating an existing Jest suite mostly mechanical.

A regra de decisão: o que você está testando?

A divisão honesta não é recursos contra dependências. É se os seus testes precisam de um ambiente com formato de navegador e de um transform de build.

O que você está testandoEscolhaPor quê
Uma biblioteca npm publicadanode:testZero dependências no caminho de teste, e nada para manter em cadência de release
Um serviço de backend ou CLInode:testSem DOM, sem transforms; a superfície embutida dá conta
Um app React, Vue ou SvelteVitestTestes de componente precisam do pipeline de transform do Vite e de um DOM
Qualquer coisa já em ViteVitestA configuração de teste é a do seu app; sem um segundo grafo de módulos para manter
Uma suíte Jest que você quer migrarVitestexpect e mocks compatíveis com Jest tornam o trabalho quase mecânico
Uma suíte que precisa de benchmarks ou testes de tipoVitestNão existe equivalente embutido
Um monorepo com os doisAmbosA escolha do runner é por pacote; não há regra contra misturar

Custo de inicialização e tempo de CI

O argumento prático para o runner embutido no CI é que não há nada a instalar antes de rodar os testes. Em um job de CI frio, o npm ci de um framework de teste e da sua cadeia de transform é tempo de relógio real a cada execução, gasto antes de uma única asserção rodar.

  • node:test não adiciona nada para instalar nem para resolver; node --test roda contra o Node que já está no runner.
  • O Vitest puxa Vitest mais Vite mais as dependências transitivas, e aquece um pipeline de transform na primeira execução.
  • Em contrapartida, o sharding do Vitest pode cortar o tempo total de relógio de uma suíte grande muito mais do que o tempo de instalação custa.

Rodando cada um no CI

GitHub Actions steps
# node:test - nothing to install
- run: node --test --experimental-test-coverage \
    --test-reporter=junit --test-reporter-destination=junit.xml \
    --test-reporter=lcov --test-reporter-destination=lcov.info

# Vitest
- run: npm ci
- run: npx vitest run --coverage --reporter=junit --outputFile=junit.xml

# Vitest, sharded across 4 machines
- run: npx vitest run --shard=${{ matrix.shard }}/4

Dá para migrar entre eles?

Em parte, e a direção importa. Ir do Vitest para o node:test é mecânico para testes unitários simples: o formato describe/it é o mesmo, e o trabalho principal é reescrever asserções expect(...) para node:assert e remapear chamadas de mock. Migrar testes de componente não é reescrita, é reconstrução, porque o pipeline de transform e o DOM não têm equivalente embutido.

  • Asserções: expect(a).toBe(b) vira assert.strictEqual(a, b). Quase tudo localizar e substituir, com cuidado na igualdade profunda.
  • Mocks: vi.fn() vira mock.fn(); vi.mock() vira mock.module(), cuja API não é idêntica.
  • Snapshots: os dois têm, mas os formatos de arquivo diferem, então conte com regenerar em vez de portar.
  • Testes de componente: não há caminho. Estes ficam no Vitest.

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

Construir um app Vite ou TypeScript e querer mocks, snapshots e coverage prontos de fábrica: Vitest. Escrever uma biblioteca que valoriza zero dependências e inicialização rápida: node:test. A maioria dos times de app escolhe Vitest; minimalistas e autores de libs cada vez mais usam node:test.

Perguntas frequentes

O test runner embutido do Node está pronto para produção?
Sim. O node:test está marcado como Stable desde o Node 20, não experimental. O teste de snapshot estabilizou na v22.3.0 e o setup e teardown globais chegaram na v24. A única parte ainda atrás de uma flag experimental é a cobertura de código, via --experimental-test-coverage.
O node:test suporta mocking?
Sim, e de forma mais completa do que a maioria das comparações sugere. Ele faz mock de funções, métodos, propriedades, getters e setters, e de módulos inteiros via mock.module(), além de timers incluindo setTimeout, setInterval e Date por meio de mock.timers.
O node:test roda TypeScript?
Sim. O Node remove tipos nativamente, e passar --no-strip-types estende os padrões padrão de arquivo de teste para incluir .ts, .mts e .cts. Você não precisa de um passo de transform separado para TypeScript direto, embora qualquer coisa que dependa de plugins do Vite ou aliases de caminho ainda exija configuração.
O Vitest é mais rápido que o node:test?
Depende de que parte você mede. O node:test evita instalar e resolver um framework, então em suítes pequenas costuma vencer no tempo total de relógio do CI. O Vitest suporta sharding entre máquinas, o que vence de forma decisiva assim que a suíte fica grande. Meça a sua divisão entre instalar e executar em vez de presumir.
Posso usar node:test para testes de componente React?
Na prática não. Testar componentes exige um DOM e um pipeline de transform de JSX, e o node:test não fornece nenhum dos dois. O Vitest herda ambos do Vite. Se você testa componentes, só isso já decide a comparação.
O node:test funciona com ferramentas de relatório e cobertura de CI?
Sim. Ele traz os reporters junit e lcov, então JUnit XML para relatório de testes e lcov para upload de cobertura funcionam sem pacotes adicionais. Isso remove o que era o argumento mais forte contra ele no CI.
Como migro do Vitest para o node:test?
Para testes unitários simples é quase todo mecânico: a estrutura describe/it é aproveitada, asserções expect(...) viram chamadas de node:assert, e vi.fn()/vi.mock() viram mock.fn()/mock.module(). Os formatos de snapshot diferem, então planeje regenerá-los. Testes de componente não migram e devem ficar no Vitest.
Autores de biblioteca devem usar Vitest ou node:test?
node:test, na maioria dos casos. Uma biblioteca publicada se beneficia de não ter nada no caminho de teste para manter atualizado, nenhuma cadeia de transform para depurar contra as configurações dos consumidores, e nenhuma cadência de release de framework para acompanhar. O runner embutido hoje cobre o que suítes de teste de biblioteca normalmente precisam.

Guias relacionados