O Teste de Carga com k6 mede como uma aplicação se comporta quando recebe tráfego concorrente. A ferramenta permite definir usuários virtuais, taxa de chegada, estágios, checks, thresholds e métricas para validar latência, erros e capacidade.
Um teste de carga útil não tenta apenas gerar o maior número de requisições. Ele simula padrões próximos da produção, protege o ambiente, define critérios de aprovação e correlaciona resultados com CPU, memória, event loop, banco e dependências externas.
Neste guia, você aprenderá scripts k6, VUs, cenários, executors, checks, thresholds, métricas, dados, autenticação, CI, observabilidade, testes distribuídos e boas práticas para APIs Node.js.
O que é k6?
k6 é uma ferramenta de performance testing com scripts em JavaScript. A documentação oficial para executar k6 apresenta usuários virtuais, duração, stages e modos local, distribuído e cloud. A documentação oficial de cenários explica executors e modelos de carga.
Para métricas da aplicação, consulte Métricas Prometheus no Node.js. Para logs durante o teste, veja Logs com Pino no Node.js.
Instalação
Instale pelo gerenciador recomendado para seu sistema ou execute em container:
docker run --rm -i grafana/k6 run - < script.jsFixe uma versão da imagem no CI para manter reprodutibilidade.
Primeiro script
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 10,
duration: '30s'
};
export default function () {
const response = http.get(
`${__ENV.BASE_URL}/health`
);
check(response, {
'status é 200': res => res.status === 200
});
sleep(1);
}Execute:
BASE_URL=https://staging.example.com k6 run script.jsInit context e VU code
Código no escopo do módulo executa durante inicialização e pode carregar dados e definir opções. A função default executa repetidamente por cada VU.
Não faça uma chamada HTTP de setup diretamente no init context. Use setup() quando precisar preparar dados.
Usuários virtuais
VUs são loops concorrentes que executam o cenário. Dez VUs não significam necessariamente dez requisições por segundo; a taxa depende da duração de cada iteração e de sleeps.
Modelo fechado
No modelo fechado, a quantidade de VUs é controlada. Se a aplicação fica lenta, cada VU completa menos iterações e a taxa diminui.
export const options = {
scenarios: {
load: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 50 },
{ duration: '5m', target: 50 },
{ duration: '2m', target: 0 }
]
}
}
};Modelo aberto
No modelo aberto, a taxa de chegada é controlada. Se o sistema fica lento, k6 precisa de mais VUs para manter a taxa.
export const options = {
scenarios: {
api: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 50,
maxVUs: 200
}
}
};Esse modelo revela saturação com mais clareza, mas pode produzir carga muito alta quando o sistema degrada.
Dropped iterations
Se não há VUs suficientes para manter a taxa, k6 registra iterações descartadas. Isso indica configuração inadequada ou sistema lento.
Tipos de teste
- Smoke: carga mínima para validar script.
- Average load: tráfego esperado.
- Stress: acima da capacidade normal.
- Spike: aumento repentino.
- Soak: carga prolongada.
- Breakpoint: busca o limite.
Cada tipo responde a uma pergunta diferente.
Smoke test
export const options = {
vus: 1,
iterations: 1
};Execute antes de testes maiores para confirmar URL, credenciais, dados e checks.
Checks
check(response, {
'status 200': res => res.status === 200,
'JSON válido': res => {
try {
return Boolean(res.json('id'));
} catch {
return false;
}
},
'menos de 500 ms': res => res.timings.duration < 500
});Checks registram sucesso e falha, mas não encerram o teste automaticamente.
Thresholds
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: [
'p(95)<500',
'p(99)<1000'
],
checks: ['rate>0.99']
}
};Thresholds definem critérios de aprovação e fazem k6 retornar código de erro quando violados.
SLO e thresholds
Use objetivos reais. Um threshold arbitrário de 100 ms pode ser inviável; um limite de 5 segundos pode esconder experiência ruim.
Métricas nativas
http_req_duration;http_req_failed;http_reqs;iteration_duration;iterations;vus;vus_max;data_received;data_sent.
Separando etapas HTTP
http_req_duration inclui receiving, sending, waiting e outros componentes. Analise DNS, conexão, TLS, blocked e waiting para descobrir onde o tempo é gasto.
Métricas customizadas
import {
Counter,
Rate,
Trend
} from 'k6/metrics';
const ordersCreated = new Counter('orders_created');
const businessErrors = new Rate('business_errors');
const checkoutDuration = new Trend(
'checkout_duration',
true
);Registre valores:
ordersCreated.add(1);
businessErrors.add(response.status === 409);
checkoutDuration.add(response.timings.duration);Tags
http.get(url, {
tags: {
name: 'GET /products/:id',
scenario: 'catalog'
}
});Use rotas normalizadas. Não coloque IDs ou URLs completas como tags.
Groups
import { group } from 'k6';
group('checkout', () => {
// requests do fluxo
});Groups organizam resultados, mas não substituem cenários.
Fluxo com múltiplas requests
export default function () {
const product = http.get(
`${baseUrl}/products/42`
);
const order = http.post(
`${baseUrl}/orders`,
JSON.stringify({
productId: product.json('id'),
quantity: 1
}),
{
headers: {
'content-type': 'application/json',
authorization: `Bearer ${token}`
}
}
);
check(order, {
'pedido criado': res => res.status === 201
});
}Think time
Usuários reais não enviam requests continuamente. Use sleep() ou pacing baseado em dados para representar pausas.
Não use sleep no modelo de taxa sem entender
Arrival-rate agenda iterações independentemente da duração. Sleeps aumentam VUs necessários, mas não reduzem a taxa configurada.
setup()
export function setup() {
const response = http.post(
`${baseUrl}/test/login`,
JSON.stringify(credentials),
{ headers: { 'content-type': 'application/json' } }
);
return {
token: response.json('token')
};
}
export default function (data) {
http.get(`${baseUrl}/profile`, {
headers: {
authorization: `Bearer ${data.token}`
}
});
}Dados retornados são serializados e compartilhados. Não retorne objetos gigantes.
Credencial única
Um único usuário pode criar lock e cache irreais. Use um pool de contas de teste ou tokens diferentes.
SharedArray
import { SharedArray } from 'k6/data';
const users = new SharedArray('users', () =>
JSON.parse(open('./users.json'))
);Carregue arquivos no init context. Use dados sintéticos.
Dados únicos por VU
Use __VU, __ITER ou APIs de execution context para selecionar registros sem colisão. A interface recomendada pode variar entre versões.
Idempotência
Ao testar POST com retry ou falha de rede, envie idempotency keys únicas.
Consulte Idempotência em APIs Node.js.
Ambiente de teste
Não execute carga pesada em produção sem aprovação, limites e observabilidade. Um teste pode:
- derrubar serviços;
- enviar e-mails;
- cobrar pagamentos;
- encher banco;
- acionar fornecedores;
- gerar custos;
- afetar clientes.
Staging representativo
Capacidade, quantidade de réplicas, banco, cache e limites precisam ser conhecidos. Um staging pequeno não prevê diretamente produção.
Teste em produção
Quando necessário, use:
- janela controlada;
- tenant de teste;
- feature flags;
- kill switch;
- alertas;
- limites progressivos;
- responsáveis presentes.
Ramp-up
Comece gradualmente para observar erros e cancelar antes do impacto máximo.
Stress e breakpoint
Aumente carga em etapas e identifique:
- primeiro SLO violado;
- recurso saturado;
- ponto de fila crescente;
- taxa máxima sustentável;
- comportamento de recuperação.
Soak test
Execute carga normal por horas para descobrir:
- memory leak;
- pool esgotado;
- fragmentação;
- fila acumulada;
- token expirando;
- rotação de logs;
- GC crescente.
Node.js durante carga
Monitore:
- CPU;
- RSS e heap;
- event loop delay;
- event loop utilization;
- GC;
- active handles;
- HTTP sockets;
- worker pool;
- errors.
Consulte Event Loop no Node.js.
Banco de dados
Observe:
- conexões ativas;
- espera por pool;
- locks;
- queries lentas;
- CPU;
- I/O;
- cache hit;
- replica lag.
Consulte Pool PostgreSQL no Node.js.
Dependências externas
Não envie carga não autorizada a fornecedores. Use mocks de rede, sandbox ou limite a taxa contratada.
Cache
Teste cache frio e quente separadamente. Um resultado excelente após aquecimento pode esconder startup ruim.
Warm-up
Antes da janela medida, aqueça JIT, pool, cache e conexões. Não descarte warm-up quando o objetivo é medir cold start.
Coordinated omission
Modelos fechados podem reduzir a taxa quando o sistema fica lento, escondendo solicitações que teriam chegado. Arrival-rate ajuda a evitar essa distorção.
Throughput versus latência
Mais throughput pode aumentar latência. Encontre a região em que o sistema atende SLO com margem, não apenas o pico absoluto.
Percentis
Média esconde caudas. Use p90, p95, p99 e taxa de erro. Uma pequena porcentagem lenta pode afetar muitos usuários em alto volume.
Distribuição local
Um gerador único pode se tornar gargalo de CPU ou rede. Monitore a máquina do k6 e distribua quando necessário.
k6 Operator
O k6 Operator distribui testes em Kubernetes por um recurso TestRun. Use quando uma máquina não gera a carga necessária.
Cloud
Grafana Cloud k6 executa testes distribuídos e armazena resultados. Avalie custo, dados e regiões.
Saídas
k6 pode enviar métricas para sistemas como Prometheus remote write, InfluxDB, JSON e Grafana Cloud, conforme versão e extensão.
Dashboards
Compare lado a lado:
- k6 request rate;
- latência;
- erros;
- CPU;
- event loop;
- banco;
- cache;
- fila;
- deploys.
CI
Em pull requests, execute smoke ou teste pequeno. Testes grandes podem rodar nightly ou antes de release.
k6 run \
-e BASE_URL="$STAGING_URL" \
tests/performance/smoke.jsThreshold no CI
O pipeline deve falhar quando thresholds não são atendidos. Preserve o resumo como artifact.
Comparação com baseline
Uma mudança pequena pode ser regressão mesmo dentro do threshold. Compare versões sob ambiente e dados semelhantes.
Variabilidade
Execute múltiplas vezes e observe intervalos. Infraestrutura compartilhada pode gerar ruído.
Browser testing
k6 browser mede experiência de navegador e métricas frontend. Ele complementa testes de protocolo HTTP, mas exige mais recursos.
WebSockets e gRPC
k6 possui APIs para WebSocket e gRPC. Modele conexões longas, mensagens e reconexão de acordo com o protocolo.
Segurança do script
Não versione:
- senhas;
- tokens;
- cookies reais;
- dados pessoais;
- URLs internas sensíveis.
Use secret source ou variáveis protegidas conforme a execução.
Limpeza
Use teardown() ou rotina externa para remover dados criados. Não dependa apenas de expiração.
Teste destrutivo
Separe endpoints de teste ou tenants. Nunca misture dados de carga com produção sem identificação e política de remoção.
Erros comuns
- Gerar máximo imediatamente: não há diagnóstico gradual.
- Sem thresholds: teste não aprova nem reprova.
- Apenas média: cauda fica escondida.
- Um usuário: locks e cache ficam irreais.
- Staging pequeno interpretado como produção: capacidade é estimada incorretamente.
- Gerador saturado: sistema parece melhor do que é.
- Sem observabilidade: gargalo não é encontrado.
- Carga em fornecedor: limites e custos são violados.
Boas práticas
- Defina a pergunta do teste.
- Use tráfego realista.
- Comece com smoke.
- Configure thresholds.
- Use percentis.
- Diferencie modelos aberto e fechado.
- Monitore toda a stack.
- Aumente carga gradualmente.
- Proteja dados e ambiente.
- Registre baseline e resultados.
Conclusão
O Teste de Carga com k6 transforma desempenho em um critério verificável. VUs, arrival rate, checks e thresholds permitem simular tráfego e bloquear regressões.
O resultado só é útil quando o ambiente é conhecido e a aplicação está observada. Com percentis, métricas do Node.js, banco, ramp-up e dados seguros, k6 ajuda a encontrar a capacidade sustentável antes que usuários descubram o limite em produção.


