Uma query de banco que roda em 30ms com 100 linhas pode dar timeout com 1 milhão. Os testes funcionais nunca vão detectar isso porque rodam um cenário por vez. Testes de performance simulam o tráfego concorrente que revela essas falhas: load testing para tráfego normal, stress testing para encontrar o ponto de ruptura, spike testing para picos súbitos. Soak testing expõe vazamentos de memória que só aparecem após horas.

Por que testes de performance importam

Um formulário de login que responde em 200ms está ótimo. Com 5 segundos, perde usuários. Com 30 segundos sob carga, derruba o servidor.

Bugs de performance costumam ser os mais difíceis de corrigir: exigem mudanças arquiteturais, não simples correções de código. Encontrá-los cedo faz diferença.

Falhas de performance comuns:
  • Queries de banco que funcionam com 100 linhas e dão timeout com 1 milhão
  • Endpoints de API que aguentam 10 usuários concorrentes e falham com 100
  • Vazamentos de memória que se acumulam após horas de uso
  • Integrações com terceiros que viram gargalo sob carga

Tipos de testes de performance

Load testing

Simula a carga esperada em produção para verificar se o sistema performa dentro dos limites aceitáveis.

Pergunta: O sistema aguenta o tráfego normal? Exemplo: Nossa app tem 500 usuários simultâneos durante o horário comercial. Rode 500 usuários virtuais por 30 minutos e meça os tempos de resposta. Resultados aceitáveis:
  • Tempo de resposta no percentil 95 abaixo de 1 segundo
  • Taxa de erro abaixo de 1%
  • Sem vazamentos de memória

Stress testing

Empurra o sistema além dos limites para encontrar o ponto de ruptura.

Pergunta: Como o sistema se comporta quando sobrecarregado? Ele falha de forma controlada? Exemplo: Comece com 100 usuários, aumente 100 a cada minuto até o sistema quebrar. Observe quando os erros começam, como o sistema falha e se ele se recupera.

Spike testing

Aumento massivo e súbito de carga, seguido de retorno ao normal.

Pergunta: O sistema aguenta picos súbitos de tráfego? Exemplo: Carga normal é de 100 usuários. De repente, salta para 1.000 por 2 minutos, depois volta para 100. Cenários preocupantes: Menções em redes sociais, matérias de jornal, flash sales.

Soak testing (teste de resistência)

Carga sustentada por um período prolongado.

Pergunta: Vazamentos de memória ou degradação de performance se acumulam ao longo do tempo? Exemplo: 200 usuários simultâneos por 8 horas. Monitore uso de memória e tendências de tempo de resposta.

k6: a ferramenta moderna de load testing

O k6 é a ferramenta de load testing mais acessível para QA: baseada em JavaScript, roda pelo CLI, integra com CI.

Instalação

# macOS
brew install k6

# Windows (winget)
winget install k6

# Docker
docker pull grafana/k6

Seu primeiro load test

// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';

// Configuração do teste
export const options = {
  vus: 50,           // Usuários virtuais
  duration: '30s',  // Rodar por 30 segundos
};

export default function() {
  // Fazer uma requisição
  const response = http.get('https://lab.becomeqa.com/api/products');
  
  // Assertions
  check(response, {
    'status é 200': (r) => r.status === 200,
    'tempo de resposta < 500ms': (r) => r.timings.duration < 500,
    'tem produtos': (r) => JSON.parse(r.body).length > 0,
  });
  
  // Aguardar entre iterações (simula comportamento real do usuário)
  sleep(1);
}

Executar:

k6 run load-test.js

Saída:

✓ status é 200              100.00% ✓ 1500  ✗ 0
✓ tempo de resposta < 500ms  95.33%  ✓ 1430  ✗ 70
✓ tem produtos              100.00% ✓ 1500  ✗ 0

http_req_duration..............: avg=185ms min=45ms  med=160ms  max=1.2s    p(90)=350ms p(95)=480ms
http_reqs......................: 1500   49.91/s

Cenários de ramp-up

Mais realista do que atingir a carga total imediatamente:

export const options = {
  stages: [
    { duration: '2m', target: 100 },  // Subir para 100 usuários em 2 minutos
    { duration: '5m', target: 100 },  // Manter 100 usuários por 5 minutos
    { duration: '2m', target: 200 },  // Subir para 200
    { duration: '5m', target: 200 },  // Manter 200 usuários
    { duration: '2m', target: 0 },    // Descer
  ],
};

Testando um endpoint de API com autenticação

import http from 'k6/http';
import { check, group } from 'k6';

export const options = {
  stages: [
    { duration: '1m', target: 50 },
    { duration: '3m', target: 50 },
    { duration: '1m', target: 0 },
  ],
  thresholds: {
    'http_req_duration': ['p(95)<500'],  // 95% das requisições abaixo de 500ms
    'http_req_failed': ['rate<0.01'],     // Menos de 1% de erros
  },
};

export function setup() {
  // Login uma vez, retorna token para todos os usuários virtuais
  const res = http.post('https://api.minhaapp.com/auth/login', JSON.stringify({
    email: 'load-test@minhaapp.com',
    password: 'SenhaLoadTest1',
  }), { headers: { 'Content-Type': 'application/json' } });
  
  return { token: JSON.parse(res.body).token };
}

export default function(data) {
  const headers = {
    'Authorization': `Bearer ${data.token}`,
    'Content-Type': 'application/json',
  };
  
  group('Listar produtos', () => {
    const res = http.get('https://api.minhaapp.com/products', { headers });
    check(res, { 'produtos carregados': (r) => r.status === 200 });
  });
  
  group('Detalhe do produto', () => {
    const res = http.get('https://api.minhaapp.com/products/1', { headers });
    check(res, { 'detalhe do produto carregado': (r) => r.status === 200 });
  });
  
  sleep(Math.random() * 3 + 1);  // Tempo de espera aleatório de 1 a 4 segundos
}

Thresholds de performance

Defina critérios de aprovação/reprovação:

export const options = {
  thresholds: {
    // 95% das requisições devem completar abaixo de 500ms
    'http_req_duration': ['p(95)<500'],
    
    // Threshold para endpoint específico
    'http_req_duration{name:products}': ['p(95)<300'],
    
    // Menos de 0,1% de erros permitidos
    'http_req_failed': ['rate<0.001'],
    
    // Threshold de métrica customizada
    'checkout_duration': ['p(90)<2000'],
  },
};

Se os thresholds não forem atingidos, o k6 sai com código não-zero e o CI falha automaticamente.

O que medir

Percentis de tempo de resposta:
  • p50 (mediana): 50% das requisições são mais rápidas que isso
  • p90: 90% das requisições são mais rápidas que isso
  • p95: 95% das requisições são mais rápidas que isso
  • p99: A cauda lenta, o que os 1% piores experimentam
Taxa de erro: Percentual de requisições que falham (5xx, timeouts) Throughput: Requisições por segundo que o sistema processa Usuários concorrentes: Quantos usuários estão ativos simultaneamente Utilização de recursos: CPU, memória, conexões de banco (medidos no servidor)

Integrando com CI

# GitHub Actions
performance-test:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    
    - name: Instalar k6
      run: |
        curl https://github.com/grafana/k6/releases/download/v0.50.0/k6-v0.50.0-linux-amd64.tar.gz -L | tar xvz
        sudo mv k6-v0.50.0-linux-amd64/k6 /usr/local/bin
    
    - name: Rodar load test
      run: k6 run load-tests/api-load-test.js
      env:
        BASE_URL: ${{ secrets.STAGING_URL }}
    
    - name: Upload dos resultados
      uses: actions/upload-artifact@v4
      if: always()
      with:
        name: k6-results
        path: k6-results.json

Problemas comuns de performance para investigar

Queries de banco lentas:
  • Monitore o tempo de execução das queries sob carga
  • Queries N+1 (1 query por linha em vez de 1 no total)
  • Índices ausentes
Vazamentos de memória:
  • Uso de memória crescendo ao longo do tempo sem diminuir
  • Detectados em soak tests
Esgotamento de pool de conexões:
  • Conexões de banco acabam sob alta concorrência
  • Erro: "too many connections"
Gargalos em serviços de terceiros:
  • API externa que fica lenta sob carga
  • Gateway de pagamento com rate limits
Cache ausente:
  • Mesmos dados buscados do banco repetidamente: deveriam estar em cache

Resumo

| Tipo de teste | Propósito | Duração |

|--------------|-----------|---------|

| Load test | Verificar performance com carga normal | 30min a 2h |

| Stress test | Encontrar o ponto de ruptura | Até a falha |

| Spike test | Aguentar picos súbitos de tráfego | Picos curtos |

| Soak test | Detectar vazamentos de memória | Horas |

Fundamentos do k6:
  • vus — usuários virtuais
  • duration — quanto tempo rodar
  • stages — padrões de ramp-up
  • thresholds — critérios de aprovação/reprovação
  • check() — assertions por requisição

Testes de performance são separados dos testes funcionais: ferramentas diferentes, objetivos diferentes. Testes funcionais verificam correção; testes de performance verificam velocidade e confiabilidade sob carga. Os dois são necessários antes de fazer deploy.

→ Veja também: Testes de Performance com k6: O Primeiro Teste de Carga do Engenheiro QA | A Pirâmide de Testes Explicada para Engenheiros QA | Testes de API 101: Tudo que Todo Engenheiro QA Precisa Saber em 2026