Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Undici MockAgent no Node.js

Atualizado em: 13 de setembro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

O Undici MockAgent no Node.js intercepta requisições feitas pelas APIs do Undici e devolve respostas programadas, sem acessar a rede. Ele é útil para testar clientes HTTP, integrações externas, retries, timeouts e parsing, mantendo controle sobre método, caminho, headers, body e quantidade de chamadas.

MockAgent atua como um Dispatcher. Para interceptar fetch ou request, ele pode ser definido como dispatcher global ou passado por requisição. Requisições sem handler podem ser bloqueadas, impedindo acesso acidental à internet durante testes.

Neste guia, você aprenderá a configurar MockAgent, criar interceptors, simular respostas e erros, validar chamadas pendentes, controlar conexões reais e integrar ao Node Test Runner.

O que é MockAgent?

A documentação oficial do MockAgent do Undici define a classe como um Dispatcher estável que intercepta requests e devolve respostas mockadas.

Ele não intercepta sozinho. É necessário:

  1. criar o MockAgent;
  2. obter um MockPool ou MockClient;
  3. registrar interceptors;
  4. definir o dispatcher;
  5. fechar após o teste.

Instalação

npm install --save-dev undici

Embora Node.js use Undici internamente para fetch, instalar o pacote oferece acesso explícito às APIs de mock e fixa a versão usada no teste.

Configuração básica

import {
  MockAgent,
  setGlobalDispatcher
} from 'undici';

export const mockAgent = new MockAgent();
mockAgent.disableNetConnect();
setGlobalDispatcher(mockAgent);

disableNetConnect() faz qualquer request não interceptado falhar.

Primeiro interceptor

const pool = mockAgent.get('https://api.example.com');

pool
  .intercept({
    method: 'GET',
    path: '/status'
  })
  .reply(200, {
    status: 'available'
  }, {
    headers: {
      'content-type': 'application/json'
    }
  });

Depois:

const response = await fetch(
  'https://api.example.com/status'
);

const body = await response.json();

Integração com Node Test Runner

import {
  before,
  after,
  afterEach
} from 'node:test';
import {
  Agent,
  MockAgent,
  setGlobalDispatcher
} from 'undici';

const mockAgent = new MockAgent();

before(() => {
  mockAgent.disableNetConnect();
  setGlobalDispatcher(mockAgent);
});

afterEach(() => {
  mockAgent.assertNoPendingInterceptors();
  mockAgent.clearCallHistory();
});

after(async () => {
  await mockAgent.close();
  setGlobalDispatcher(new Agent());
});

Consulte Node Test Runner.

Dispatcher global

setGlobalDispatcher() afeta APIs que usam o dispatcher global, como fetch e request. Ele não substitui instâncias de Pool ou Client criadas separadamente.

Quando seu código aceita um dispatcher, prefira injeção explícita:

export async function getStatus({ dispatcher }) {
  const response = await fetch(
    'https://api.example.com/status',
    { dispatcher }
  );

  return response.json();
}

Mock por requisição

const response = await fetch(url, {
  dispatcher: mockAgent
});

Esse modelo evita alterar estado global e facilita paralelismo.

Matching de origem

String exata:

mockAgent.get('https://api.example.com');

Regex:

mockAgent.get(/^https:\/\/api\.example\.com$/);

Função:

mockAgent.get(origin =>
  origin === 'https://api.example.com'
);

Prefira string exata quando possível.

Matching de query

pool.intercept({
  method: 'GET',
  path: '/products?limit=10&status=active'
}).reply(200, { data: [] });

A ordem e serialização podem afetar o match. Para queries dinâmicas, use matcher compatível ou normalize a URL no cliente.

Matching de body

pool.intercept({
  method: 'POST',
  path: '/charges',
  body: JSON.stringify({
    amount: 1000,
    currency: 'BRL'
  })
}).reply(201, {
  id: 'charge-1',
  status: 'approved'
});

O body precisa corresponder à serialização esperada.

Matching de headers

pool.intercept({
  method: 'GET',
  path: '/account',
  headers: {
    authorization: 'Bearer test-token'
  }
}).reply(200, {
  id: 'account-1'
});

Nunca use tokens reais em fixtures.

Resposta com função

pool.intercept({
  method: 'GET',
  path: '/time'
}).reply(200, () => ({
  generatedAt: '2026-09-13T00:00:00Z'
}));

Use valores fixos para manter o teste determinístico.

Resposta 500

pool.intercept({
  method: 'GET',
  path: '/report'
}).reply(500, {
  title: 'Internal Server Error',
  status: 500
}, {
  headers: {
    'content-type': 'application/problem+json'
  }
});

Veja Problem Details no Node.js.

Erro de rede

MockAgent permite responder com erro:

pool.intercept({
  method: 'GET',
  path: '/report'
}).replyWithError(
  new Error('Connection reset')
);

Teste separadamente erros de rede e status HTTP.

Resposta repetida

pool.intercept({
  method: 'GET',
  path: '/health'
}).reply(200, { status: 'ok' })
  .times(3);

O interceptor pode ser consumido uma quantidade definida de vezes.

Persistência

pool.intercept({
  method: 'GET',
  path: '/config'
}).reply(200, { feature: true })
  .persist();

Use persist somente para respostas comuns. Ele pode esconder chamadas em excesso.

Retries

Registre interceptors na ordem:

pool.intercept({
  method: 'GET',
  path: '/data'
}).reply(503, {});

pool.intercept({
  method: 'GET',
  path: '/data'
}).reply(503, {});

pool.intercept({
  method: 'GET',
  path: '/data'
}).reply(200, { value: 42 });

Assim, você verifica retry sem contador global. Consulte Retry com Backoff no Node.js.

Requests pendentes

mockAgent.assertNoPendingInterceptors();

O método falha quando um interceptor esperado não foi consumido. Isso detecta código que deixou de chamar uma dependência importante.

pendingInterceptors

const pending = mockAgent.pendingInterceptors();
console.log(pending);

Use para diagnóstico quando a assertion falhar.

Call history

const mockAgent = new MockAgent({
  enableCallHistory: true
});

// após requests
const history = mockAgent.getCallHistory();
const first = history?.firstCall();

A call history registra método, origem, path, headers e body conforme disponíveis. Limpe entre testes.

Assertions de chamadas

Use call history apenas quando o contrato de request é o comportamento testado. Evite assertions excessivas sobre ordem e headers internos.

Bloqueando rede

mockAgent.disableNetConnect();

Esse deve ser o default em testes unitários e de integração isolados.

Permitindo localhost

mockAgent.enableNetConnect(host => {
  return host.startsWith('127.0.0.1') ||
    host.startsWith('localhost');
});

Útil quando a aplicação local é real, mas dependências externas são mockadas.

Permitindo host específico

mockAgent.enableNetConnect('localhost:54321');

Evite allowlists amplas como regex que aceita qualquer domínio.

activate e deactivate

mockAgent.deactivate();
mockAgent.activate();

Normalmente não é necessário. Alterar estado no meio da suíte pode criar concorrência.

Fechando recursos

await mockAgent.close();

O método fecha pools e limpa histórico. Esquecer pode manter handles ativos.

MockClient versus MockPool

Com uma conexão, get() pode retornar MockClient; com várias, MockPool. Na maioria dos testes, use a interface retornada sem depender da classe concreta.

Clientes que criam Pool

O dispatcher global não intercepta um Pool criado diretamente. Injete o mock:

const pool = mockAgent.get(origin);
const client = new PaymentsClient({ dispatcher: pool });

Dependency injection melhora testabilidade sem monkeypatch.

fetch nativo

Versões atuais do Node.js usam Undici para fetch, mas diferenças entre a versão interna e o pacote instalado podem existir. Teste na versão real do runtime.

Consulte Fetch Nativo no Node.js.

MockAgent versus MSW

Mock Service Worker no Node.js oferece handlers compartilháveis com navegador, GraphQL, SSE e WebSocket. MockAgent é mais direto para clientes Undici e controle de Dispatcher.

Testes paralelos

Um dispatcher global compartilhado pode causar colisões. Prefira um MockAgent por arquivo ou dispatcher por request. Não compartilhe interceptors mutáveis entre testes concorrentes.

TypeScript

Crie helpers tipados:

function mockJson<T>(
  pool,
  path: string,
  body: T,
  statusCode = 200
) {
  return pool.intercept({
    method: 'GET',
    path
  }).reply(statusCode, body, {
    headers: {
      'content-type': 'application/json'
    }
  });
}

CI

Com rede desativada, a suíte não depende da internet:

- run: npm ci
- run: npm run typecheck
- run: npm test

Consulte CI para Node.js com GitHub Actions.

Segurança

  • Use domínios example.com.
  • Não use tokens reais.
  • Desative rede.
  • Limpe call history.
  • Feche o agent.
  • Fixe a versão do Undici.
  • Não registre bodies sensíveis.

Erros comuns

  • Sem dispatcher: interceptor não é usado.
  • Pool criado separadamente: global não intercepta.
  • Rede habilitada: teste acessa produção.
  • Interceptor persistente: chamadas extras passam.
  • Não verificar pendentes: request esperado não ocorre.
  • Estado global paralelo: testes colidem.
  • Não fechar: processo mantém handles.
  • Confundir erro de rede e 500: lógica fica incompleta.

Configuração recomendada

import {
  Agent,
  MockAgent,
  setGlobalDispatcher
} from 'undici';

export function createHttpMocks() {
  const mockAgent = new MockAgent({
    enableCallHistory: true
  });

  mockAgent.disableNetConnect();
  setGlobalDispatcher(mockAgent);

  return {
    mockAgent,
    pool(origin: string) {
      return mockAgent.get(origin);
    },
    async close() {
      mockAgent.assertNoPendingInterceptors();
      await mockAgent.close();
      setGlobalDispatcher(new Agent());
    }
  };
}

Conclusão

O Undici MockAgent no Node.js oferece controle direto sobre requests feitos por fetch e outras APIs do Undici. Interceptors permitem programar respostas, falhas, retries e quantidade de chamadas sem abrir uma conexão real.

Desative a rede, verifique interceptors pendentes e feche o dispatcher. Com injeção explícita em clientes próprios, MockAgent produz testes rápidos e determinísticos sem depender de monkeypatch global.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita