Node Test Runner no Node.js é o executor de testes nativo acessado pelo módulo node:test e pelo comando node --test. Ele permite criar testes unitários e de integração sem instalar um framework externo.
O runner fornece testes assíncronos, hooks, subtests, mocks, filtragem, paralelismo e relatórios. A vantagem principal é reduzir dependências e usar uma ferramenta que acompanha o runtime.
Neste guia, você aprenderá estrutura de testes, assertions, hooks, concorrência, timeouts, mocks, cobertura, CI e boas práticas.
Primeiro teste
import test from 'node:test';
import assert from 'node:assert/strict';
import { somar } from '../src/math.js';
test('soma dois números', () => {
assert.equal(somar(2, 3), 5);
});Execute:
node --testOrganização de arquivos
src/
math.js
test/
math.test.jsUse nomes previsíveis e mantenha testes próximos do domínio ou em uma pasta dedicada conforme o tamanho do projeto.
Testes assíncronos
test('busca usuário', async () => {
const user = await service.buscar('123');
assert.equal(user.id, '123');
});Retorne ou aguarde a Promise. Não finalize o teste antes da operação assíncrona.
Rejeições
await assert.rejects(
() => service.buscar('inexistente'),
{ code: 'USER_NOT_FOUND' }
);Teste códigos estáveis em vez de depender apenas de mensagens.
Suites com describe
import { describe, it } from 'node:test';
describe('calculadora', () => {
it('soma', () => {
assert.equal(somar(1, 2), 3);
});
});Hooks
import { before, after, beforeEach } from 'node:test';
before(async () => {
await database.start();
});
after(async () => {
await database.stop();
});
beforeEach(async () => {
await database.reset();
});Hooks devem ser rápidos e sempre liberar recursos.
Subtests
test('validação', async (t) => {
await t.test('rejeita email vazio', () => {
assert.throws(() => validarEmail(''));
});
await t.test('aceita email válido', () => {
assert.doesNotThrow(() => validarEmail('a@b.com'));
});
});Timeouts
test('responde rápido', { timeout: 2000 }, async () => {
await executarOperacao();
});Timeout não substitui cancelamento real. Recursos externos devem receber AbortSignal ou possuir encerramento explícito.
Testes concorrentes
Concorrência acelera suítes, mas testes que compartilham banco, arquivos ou portas podem interferir entre si. Use dados e recursos isolados.
Filtrando testes
node --test --test-name-pattern="usuário"Filtros ajudam no desenvolvimento, mas CI deve executar a suíte completa.
Only e skip
Testes podem ser pulados ou marcados para execução exclusiva. Configure revisão ou CI para impedir que um only acidental reduza a suíte publicada.
Testes parametrizados
for (const [input, expected] of [
[1, 2],
[2, 4],
[5, 10]
]) {
test(`dobra ${input}`, () => {
assert.equal(dobrar(input), expected);
});
}Mocks
O contexto de teste oferece ferramentas de mock:
test('registra evento', (t) => {
const logger = { info() {} };
const spy = t.mock.method(logger, 'info');
executar(logger);
assert.equal(spy.mock.callCount(), 1);
});Veja o artigo dedicado sobre Mocking no Node.js.
Injeção de dependência
Não tente substituir qualquer import global. Passe repositórios, relógios e clientes por parâmetro. Isso reduz acoplamento e simplifica testes.
Teste de servidor
Crie a aplicação sem abrir porta automaticamente. O teste pode iniciar em porta aleatória e encerrar no hook.
const server = app.listen(0);
await once(server, 'listening');
// chamadas HTTP
server.close();Banco de dados
Testes unitários não precisam de banco real. Testes de integração devem usar instância isolada, migrações e limpeza determinística.
Cobertura
O runtime pode gerar cobertura ao executar a suíte. Use métricas como apoio, não como substituto de cenários relevantes.
CI
npm ci
node --testUse lockfile conforme Lockfile no Node.js e divida suítes grandes com Test Sharding no Node.js.
Testes frágeis
Evite depender de tempo real, rede externa, ordem, locale ou caminhos globais. Injete relógio, use fixtures pequenas e controle o ambiente.
Erros comuns
- esquecer await;
- compartilhar estado entre testes;
- abrir recursos sem fechar;
- usar sleep para sincronização;
- testar implementação interna;
- executar rede externa;
- deixar only no código;
- usar cobertura como meta única.
Checklist
- nomes descritivos;
- assertions estritas;
- recursos encerrados;
- dados isolados;
- timeouts razoáveis;
- erros negativos testados;
- execução local igual ao CI;
- sem dependência de ordem;
- suíte rápida.
Conclusão
Node Test Runner no Node.js oferece uma base completa para testar aplicações sem dependência adicional. Ele funciona especialmente bem em projetos que valorizam APIs nativas e configuração simples.
Estruture o código para receber dependências, isole recursos externos e mantenha a suíte determinística. A qualidade dos testes depende mais do desenho da aplicação do que da quantidade de ferramentas.
Consulte a documentação oficial do test runner do Node.js e a documentação do módulo assert.


